Multiple Applications (identity provider)
Keep your host configuration and register explicit clients for applications that sign in through SqlOS.
Use SqlOS as an OpenID Connect provider when several applications should sign in with your accounts. ConfigureApplication describes the host independently of client registration. It supports the same API and MCP surfaces, branding, headless UI, and authorization seeds as UseSingleApplication, but does not create a client.
Suppose the existing host used UseSingleApplication("Acme", app => ...) at https://acme.example.com, with /api and /mcp. Preserve that topology and register its derived client explicitly before adding the second application:
using Microsoft.EntityFrameworkCore;
using SqlOS.Extensions;
builder.AddSqlOS<AppDbContext>(db => db.UseSqlServer(connectionString), options =>
{
options.ConfigureApplication("Acme", app =>
{
app.Origin = "https://acme.example.com";
app.Api = "/api";
app.Mcp = "/mcp";
app.Brand(page => page.PrimaryColor = "#0f172a");
app.Authorization(fga => fga
.ResourceType("project", "Project")
.Permission("PROJECT_READ", "Read projects", "project")
.Role("project_viewer", "Viewer").Can("PROJECT_READ"));
});
// Preserve the existing client identity and every security-relevant setting.
options.AuthServer.SeedClient(client =>
{
client.ClientId = "acme";
client.Name = "Acme";
client.ClientType = "public_pkce";
client.RequirePkce = true;
client.IsFirstParty = true;
client.Audience = "https://acme.example.com/api";
client.RedirectUris = ["https://acme.example.com/auth/callback"];
client.AllowedScopes = ["openid", "profile", "email", "offline_access"];
});
options.AuthServer.SeedClient(client =>
{
client.ClientId = "customer-portal";
client.Name = "Customer Portal";
client.ClientType = "public_pkce";
client.RequirePkce = true;
client.IsFirstParty = true;
client.Audience = "https://acme.example.com/api";
client.RedirectUris = ["https://portal.acme.example.com/auth/callback"];
client.AllowedScopes = ["openid", "profile", "email"];
});
});Keep your actual branding, permission model, tools, issuer, scopes, and client overrides when making this change. Both clients above can obtain API-audience tokens; MCP clients still request the distinct MCP resource. The existing client retains its database identity on reconciliation. No endpoint or domain-service rewrite is required.
Do not combine UseSingleApplication with explicit startup client seeds. Startup rejects that combination. Moving the issuer to another origin is a separate deployment migration, not a prerequisite for adding a client.
A dedicated host can instead live at https://id.acme.example.com. Set AuthServer.PublicOrigin to that origin and AuthServer.Issuer to https://id.acme.example.com/sqlos/auth. It publishes OIDC discovery at /sqlos/auth/.well-known/openid-configuration. Each application registers its own exact callback and configures its OIDC handler with this authority.
The README contains complete provider and ASP.NET Core relying-party programs, including a confidential partner client with a secret resolved from host configuration. The Sign in with X guide demonstrates the complete provider-to-Next.js journey.
| Concern | Single application | Explicit clients |
|---|---|---|
| Host | UseSingleApplication | ConfigureApplication; same host options |
| Browser clients | One derived first-party PKCE client | One seed, dashboard record, or API operation per client |
| Local API/MCP protection | Derived from declared paths | Same protection; configure each client's audience deliberately |
| Consent | Derived client is first-party | Third-party clients request consent; first-party clients skip it |
| Scope configuration | Application scopes also seed the derived client | Application scopes describe resources; each explicit client has its own allowlist |
| Branding and FGA | Brand, Headless, Authorization | The same methods, or the underlying seed APIs |
| Portable MCP clients | CIMD and resource indicators enabled by MCP | Same behavior when MCP is declared; DCR remains opt-in |
Clients created through the dashboard and administration API coexist with code-owned clients under distinct IDs. Startup does not overwrite operator-owned records. Application Access controls which organizations and principals can use each client; audience checks control where the resulting token is accepted.
AuthServer.EnableScim, then create a connection and group mappings through the SCIM configuration workflow. This does not replace SSO or application access policies.ConfigureApplication("SqlOS Example", ...) with Brand, Headless, and Authorization, plus explicit Next.js, Angular, and Expo clients. AppHost starts the two web clients; run Expo separately. Both web clients demonstrate hosted and headless sign-in against the same identity host.ConfigureApplication("SqlOS Todo", ...) with separate web and CLI registrations. Start AppHost, sign in at http://localhost:5090, then run dotnet run --project examples/SqlOS.Todo.Cli -- login for the device flow.ConfigureApplication provider and an Auth.js client, with third-party consent and remembered grants.The examples preserve their existing client IDs, callbacks, scopes, and audiences. ConfigureApplication changes how the host is described; the clients continue using their standard OIDC libraries.