AuthServer
Clients
Choose the right client onboarding path for owned apps, portable clients, and compatibility clients.
A client is the app that sends a user into SqlOS sign in and later exchanges the auth code for tokens.
SqlOS supports four client onboarding paths:
CIMD when the client_id is a stable HTTPS metadata documentDCR for public clients that still expect runtime registrationIf you are unsure, start with the first path.
For a one-app setup, use Single-Application Setup before reaching for explicit client seeding.
This is the default path for most teams.
Use it for:
Open Auth Server > Clients.

Recommended for first-party apps:
builder.AddSqlOS<AppDbContext>(options =>
{
options.AuthServer.SeedOwnedWebApp(
"my-web-app",
"My Web Application",
"http://localhost:3000/auth/callback",
"https://app.example.com/auth/callback");
});curl -X POST http://localhost:5062/sqlos/admin/auth/api/clients \
-b "$SQLOS_DASHBOARD_COOKIE_JAR" \
-H "Content-Type: application/json" \
-d '{
"clientId": "my-web-app",
"name": "My Web Application",
"audience": "sqlos",
"redirectUris": ["http://localhost:3000/auth/callback"]
}'The Admin API requires an operator session; see Authenticate operator API calls.
| Field | Required | Description |
|---|---|---|
clientId | Yes | Unique identifier (e.g., my-web-app) |
name | Yes | Display name |
audience | No | Token audience claim |
redirectUris | Yes | Allowed callback URIs for OAuth flows |
Use a confidential client when the OAuth callback and refresh-token exchange run
in server-side code that can protect a secret. SqlOS then requires
client_secret_basic authentication for both the authorization-code and refresh
grants. Public browser, native, and CLI clients continue to use PKCE without a
secret.
The credential belongs to the OAuth client. Creating a confidential client does
not create or require an FGA service account. Use SeedMachineClient only when
the same application also needs an FGA subject and grants for service-to-service
authorization.
For code-owned configuration, resolve the secret from the host's existing secret provider:
options.AuthServer.SeedClient(client =>
{
client.ClientId = "billing-server";
client.Name = "Billing Server";
client.ClientType = "confidential";
client.RedirectUris = ["https://billing.example.com/auth/callback"];
client.AllowedScopes = ["openid", "profile", "offline_access"];
client.ClientSecretResolver = () =>
builder.Configuration["SqlOS:Clients:BillingServer:Secret"];
});Secrets must contain 43 to 256 characters. Startup fails closed when a confidential code-owned client cannot resolve exactly one plaintext secret or compatible password hash. SqlOS stores only the slow hash.
In the dashboard, choose Owned Server Web, create the client, and copy the generated secret from the one-time reveal. The equivalent admin API flow is:
POST /sqlos/admin/auth/api/clients with
"clientType": "confidential";POST /sqlos/admin/auth/api/clients/{id}/credentials;clientSecret before leaving the response.Credential list responses contain only metadata. Create a second credential for
a deployment overlap window, move callers to it, and then revoke the old
credential. Authentication failures return a generic invalid_client response
and do not consume the authorization code or refresh token.
Use the CLI / Device OAuth dashboard preset or seed a CLI client:
builder.AddSqlOS<AppDbContext>(options =>
{
options.AuthServer.SeedCliClient(
"acme-cli",
"Acme CLI",
"https://api.acme.com",
"openid",
"profile",
"email",
"offline_access");
});CLI clients are public clients. They do not require redirect URIs and are allowed to use:
urn:ietf:params:oauth:grant-type:device_coderefresh_tokenSee CLI OAuth for the complete flow and polling rules.
Clients seeded in startup code are marked as startup managed. Their configuration is reapplied on every restart, so changes made in the dashboard will be overwritten.
Dashboard-created clients are fully editable and persist across restarts.
SqlOS can reuse an existing AuthPage browser session without asking the user to
enter a primary credential again, but only for clients marked as first-party.
Owned clients created with SeedOwnedWebApp, SeedOwnedNativeApp, or the
single-application setup are first-party automatically.
Before issuing a new authorization code, SqlOS always re-evaluates the current
organization, membership, and MFA policy. A request bound to an organization
uses that organization rather than the organization remembered by the browser
session. If organization selection or MFA is required, the user sees that step
instead of receiving a code. With prompt=none, SqlOS returns
interaction_required because the protocol forbids displaying the step.
Dynamically registered, CIMD, and other third-party clients do not receive this
silent session reuse. They require a user interaction; prompt=none returns
consent_required. This prevents a client-controlled authorization URL from
riding an unrelated signed-in browser session. Do not set IsFirstParty for an
application you do not own and trust.
Custom headless browser UIs should forward both PendingToken and MfaToken
from BuildUiUrl. The former carries organization selection; the latter carries
an MFA step-up into the headless request view. The official examples include
both automatically.
Use CIMD when a public client uses a stable HTTPS client_id that points to its own metadata document.
This is the better long-term public-client story because the client keeps its own metadata and SqlOS can fetch and cache it.
Enable it with:
builder.AddSqlOS<AppDbContext>(options =>
{
options.AuthServer.ClientRegistration.Cimd.Enabled = true;
});That path is especially useful for:
Use DCR only when a real client still expects runtime registration.
Enable it with:
builder.AddSqlOS<AppDbContext>(options =>
{
options.AuthServer.ClientRegistration.Dcr.Enabled = true;
});SqlOS keeps this path intentionally narrow in v1:
token_endpoint_auth_method=noneRegister separate clients for each frontend. The example stack seeds three:
auth.SeedOwnedWebApp("example-web", "Example Web", "http://localhost:3010/auth/callback");
auth.SeedOwnedWebApp("example-angular", "Example Angular", "http://localhost:4200/auth/callback");
auth.SeedOwnedNativeApp("example-expo", "Example Expo", "sqlos-expo://auth-callback");