AuthServer
OIDC Social Login
Configure Google, Microsoft, Apple, GitHub, and custom social providers.
SqlOS supports social login via OpenID Connect providers and GitHub OAuth. Users click a provider button, authenticate with the provider, and are linked or created in SqlOS.
Provider authentication does not override the local account lifecycle. If a provider subject or verified email maps to an existing inactive SqlOS user, the callback fails with a generic social-sign-in error, does not create a replacement identity link, and records the internal lifecycle reason in the audit log. Re-enable the existing SqlOS user explicitly before allowing another provider login.
| Provider | Key | Notes |
|---|---|---|
google | Standard OAuth 2.0 | |
| Microsoft | microsoft | Entra ID (Azure AD) |
| Apple | apple | Web only, requires Apple Developer account |
| GitHub | github | OAuth profile/email lookup through GitHub's user APIs |
| Custom | any | Any OIDC-compliant provider via discovery or manual config |
Dashboard: Auth Server > OIDC > Create Connection
Admin API:
curl -X POST http://localhost:5062/sqlos/admin/auth/api/oidc-connections \
-b "$SQLOS_DASHBOARD_COOKIE_JAR" \
-H "Content-Type: application/json" \
-d '{
"providerType": "google",
"displayName": "Google",
"clientId": "your-google-client-id",
"clientSecret": "your-google-client-secret",
"allowedCallbackUris": [
"http://localhost:5062/sqlos/auth/oidc/callback"
]
}'SqlOS owns the provider callback URI:
http://localhost:5062/sqlos/auth/oidc/callbackAdd the exact dashboard-provided URI to your provider's allowed redirect URIs.
If you build a custom headless callback with SqlOSOidcAuthService, register your app-owned callback route instead. The example API uses /api/v1/auth/oidc/callback/{connectionId} so the callback can complete the handoff before returning to the frontend.
Creation enables a connection by default. If an operator later disables it, re-enable it with:
curl -X POST http://localhost:5062/sqlos/admin/auth/api/oidc-connections/{id}/enable \
-b "$SQLOS_DASHBOARD_COOKIE_JAR"All Admin API examples require an operator session. Follow Authenticate operator API calls, or use the dashboard UI.
Frontend Backend Provider
│ │ │
├─ GET /oidc/providers ───►│ │
│◄── provider list ────────│ │
│ │ │
├─ POST /oidc/start ──────►│ │
│◄── authorizationUrl ─────│ │
│ │ │
├──── redirect to provider ────────────────────────────►│
│◄── callback with code ────────────────────────────────│
│ │ │
│ GET /oidc/callback ──►│── exchange code ──────────►│
│ │◄── user info ──────────────│
│◄── redirect with code │ │
│ │ │
├─ POST /oidc/exchange ───►│ │
│◄── login state/tokens ───│ │SqlOS treats the validated ID token as the identity anchor. The standard sub claim must be present in that signed token. When a connection uses UserInfo, SqlOS accepts the UserInfo response only when its sub exactly matches the ID-token sub using ordinal comparison; a missing or different subject rejects the login before any UserInfo claim is used.
Email and email_verified are resolved as one provenance-bound pair:
The provider callback is not an identity-claim source. Callback parameters cannot replace sub, email, or email verification. Apple's first-authorization user object is the only callback profile hint SqlOS reads, and only its bounded, sanitized first and last names may contribute to the display name.
GitHub remains a separate OAuth profile flow: SqlOS uses GitHub's stable numeric user id plus the verified primary address returned by GitHub's email API. It does not mix GitHub profile fields into the OIDC claim pipeline.
An OIDC login proves the configured provider authenticated the user; it does not automatically prove that the provider performed MFA. SqlOS reads amr and acr only from the validated, signed ID token and treats them as assurance evidence only when that specific connection opts in with an explicit allowlist.
Code-owned OIDC seeds can configure the policy without adding runtime infrastructure:
options.AuthServer.SeedOidcConnection("workforce", oidc =>
{
oidc.ProviderType = SqlOSOidcProviderType.Microsoft;
oidc.DisplayName = "Workforce Entra ID";
oidc.ClientId = builder.Configuration["Entra:ClientId"]!;
oidc.ClientSecret = builder.Configuration["Entra:ClientSecret"]!;
oidc.AllowedCallbackUris.Add(
"https://app.example.com/sqlos/auth/oidc/callback/{connectionId}");
oidc.TrustUpstreamMfa = true;
oidc.AcceptedAmrValues.Add("mfa");
oidc.AcceptedAcrValues.Add("urn:example:approved-assurance");
});TrustUpstreamMfa defaults to false. Missing evidence, disabled trust, or an unrecognized value leaves MFA unsatisfied, so an organization/client policy that requires MFA continues to local step-up. Accepted evidence keeps the primary method (for example, microsoft) and adds the separate upstream_mfa assurance method to sessions and tokens. Audit events record whether evidence was missing, untrusted, unrecognized, or accepted.
Treat provider claim semantics as part of the connection configuration. Do not copy an amr or acr value from another IdP without confirming that your provider and tenant policy assign it the assurance meaning you expect.
The normal application flow starts at /sqlos/auth/authorize; hosted AuthPage invokes the social provider and returns an OAuth authorization code to your application. Use the ASP.NET Core login quickstart for that recommended path.
If your app owns the social-provider chooser, use SqlOSOidcBrowserAuthService. It protects the provider callback with its own temporary state, returns an application authorization code, validates your PKCE verifier on exchange, and re-enters the normal organization-selection and MFA state machine. Do not compose a production flow directly from the lower-level SqlOSOidcAuthService unless you independently implement all of those controls.
app.MapPost("/api/social/start", async (
SocialStartRequest request,
SqlOSOidcBrowserAuthService browserAuth,
HttpContext httpContext,
CancellationToken ct) =>
{
var result = await browserAuth.CreateAuthorizationUrlAsync(
new SqlOSOidcAuthorizationUrlRequest(
ConnectionId: request.ConnectionId,
ClientId: "my-web-app",
RedirectUri: "https://app.example.com/auth/callback",
State: request.State,
CodeChallenge: request.CodeChallenge,
CodeChallengeMethod: "S256",
Email: request.Email),
httpContext,
ct);
return Results.Ok(new { authorizationUrl = result.AuthorizationUrl });
});
app.MapPost("/api/social/exchange", async (
SocialExchangeRequest request,
SqlOSOidcBrowserAuthService browserAuth,
HttpContext httpContext,
CancellationToken ct) =>
{
var outcome = await browserAuth.ExchangeCodeAsync(
new SqlOSPkceExchangeRequest(
Code: request.Code,
ClientId: "my-web-app",
RedirectUri: "https://app.example.com/auth/callback",
CodeVerifier: request.CodeVerifier),
httpContext,
ct);
// Branch on organization selection, MFA/enrollment, then Tokens.
return LoginOutcome(outcome);
});The client must generate an unguessable state value and an S256 PKCE verifier/challenge, persist the verifier and state in a server-side or platform-protected transaction, and compare the returned state before exchange. MapSqlOS() already maps the provider callback at /sqlos/auth/oidc/callback; the application RedirectUri above is a separate, registered OAuth client callback.
const { providers } = await apiGet("/api/v1/auth/oidc/providers");
// User clicks a provider
const { authorizationUrl } = await apiPost("/api/v1/auth/oidc/start", {
email,
connectionId: provider.connectionId,
});
window.location.href = authorizationUrl;http://localhost:5062/sqlos/auth/oidc/callbackproviderType: "google", your client ID and secretproviderType: "microsoft", your client ID and secretproviderType: "apple", the Services ID, Team ID, Key ID, and .p8 private key. SqlOS generates the client-secret JWT. Apple requires an HTTPS Return URL and does not accept localhost; follow Apple OIDC.providerType: "github", your GitHub client ID and secretread:user and user:emailSee GitHub OIDC for dashboard, API, and code-first setup.
For any OIDC-compliant provider, use discovery-based or manual configuration:
# Discovery-based (auto-fetches endpoints from .well-known)
curl -X POST http://localhost:5062/sqlos/admin/auth/api/oidc-connections \
-b "$SQLOS_DASHBOARD_COOKIE_JAR" \
-H 'Content-Type: application/json' \
-d '{
"providerType": "custom",
"displayName": "Okta",
"clientId": "...",
"clientSecret": "...",
"useDiscovery": true,
"discoveryUrl": "https://your-org.okta.com/.well-known/openid-configuration",
"allowedCallbackUris": [
"http://localhost:5062/sqlos/auth/oidc/callback"
]
}'For a provider without discovery, set useDiscovery: false and provide issuer, authorizationEndpoint, tokenEndpoint, jwksUri, and the exact allowedCallbackUris; add userinfoEndpoint when available. See Custom OIDC for a complete payload.