AuthServer
SAML SSO
Configure SAML enterprise SSO for organizations.
SqlOS supports SAML 2.0 enterprise SSO. Each organization can have its own SSO connection, and users with matching email domains are automatically routed to their organization's identity provider.
You can configure SAML in two ways:
SDK:
var draft = await adminService.CreateSsoConnectionDraftAsync(new CreateSsoConnectionDraftRequest
{
OrganizationId = org.Id,
DisplayName = "Acme Entra SSO",
PrimaryDomain = "acme.com",
AutoProvisionUsers = true,
AutoLinkByEmail = true
});Admin API:
curl -X POST http://localhost:5062/sqlos/admin/auth/api/sso-connections/draft \
-H "Content-Type: application/json" \
-d '{
"organizationId": "org_...",
"displayName": "Acme Entra SSO",
"primaryDomain": "acme.com",
"autoProvisionUsers": true,
"autoLinkByEmail": true
}'After creation, SqlOS generates two values you need for your IdP:
| Value | IdP field (Entra) |
|---|---|
| SP Entity ID | Identifier (Entity ID) |
| ACS URL | Reply URL (Assertion Consumer Service URL) |
In Microsoft Entra ID (or any SAML IdP):
curl -X POST http://localhost:5062/sqlos/admin/auth/api/sso-connections/{connectionId}/metadata \
-H "Content-Type: application/json" \
-d '{"metadataXml": "<?xml version=\"1.0\" ...>"}'The connection is now active. By default, existing organization members with verified @acme.com email addresses will be routed to Entra on their next sign-in. New users are not JIT-provisioned unless you explicitly enable that policy.
Create a portal session from the dashboard organization SSO tab or through the admin API:
curl -X POST http://localhost:5062/sqlos/admin/auth/api/sso-portal/sessions \
-H "Content-Type: application/json" \
-d '{
"organizationId": "org_...",
"provider": "microsoft-entra"
}'The response includes a setupUrl. Send that URL with your own mailer or admin workflow. The first browser open consumes the URL token and stores an HttpOnly server-side portal session cookie. Reuse requires a new setup link.
The portal is scoped to one organization and supports:
Hosts can expose the same launch behavior from their own admin surfaces by wrapping SqlOSSsoPortalService.CreateSessionAsync, as the example app does with POST /api/sso-portal-links.
Revoke a setup link or portal session:
curl -X POST http://localhost:5062/sqlos/admin/auth/api/sso-portal/sessions/{sessionId}/revoke \
-H "Content-Type: application/json" \
-d '{"reason": "customer_cancelled"}'Portal-scoped APIs live under /sqlos/admin/auth/sso-portal/api and require the portal session cookie. They cannot list users, clients, other organizations, or global dashboard pages.
Self-serve SSO setup can claim an organization's email domain before home realm discovery trusts it. The portal asks the org admin for a domain such as acme.com, creates a pending claim, and returns a DNS TXT record:
| Field | Value |
|---|---|
| Type | TXT |
| Name | _sqlos-verify.acme.com |
| Value | sqlos-domain-verification=... |
When the TXT record is visible, the portal marks the domain active. Home realm discovery checks active verified domains first, then falls back to the legacy operator-managed PrimaryDomain field. This keeps existing platform-admin setups working while making self-serve domain claims authoritative.
SqlOS has no Azure dependency for this. The default verifier uses public DNS-over-HTTPS and is registered behind ISqlOSDomainDnsVerifier, so hosts can replace it with internal DNS, provider-specific DNS APIs, or a test fake.
Activation requires a verified self-serve domain by default when the organization does not already have an operator-managed primary domain. Disable that gate only when your own host app enforces equivalent ownership outside SqlOS.
options.AuthServer.ConfigureSsoPortal(portal =>
{
portal.ReservedDomainRoots.Add("yourapp.com");
portal.RequireVerifiedDomainForActivation = true;
});Portal-created connections default to:
RequireSsoForExistingMembers = trueAllowJitProvisioning = falseInternally these map to the existing connection flags:
RequireSsoForExistingMembers maps to AutoLinkByEmailAllowJitProvisioning maps to AutoProvisionUsersWhen RequireSsoForExistingMembers is enabled, a user who already belongs to the organization and has a verified email on the SSO domain is sent to SSO. The first successful SAML sign-in links the IdP subject to that existing user. SqlOS does not create a user or membership on this path.
When AllowJitProvisioning is enabled, a successful SAML sign-in may create the missing user or organization membership when no existing organization member matches the SAML email. Keep this disabled when access should be invitation- or admin-managed.
Activation only enables the SSO connection and HRD behavior. It does not revoke active sessions. Use the separate session revocation action when an admin intentionally wants existing matching-domain sessions to sign in again through SSO.
The hosted portal is optional. Configure BuildUiUrl to hand the opened portal session to your own admin UI, and call the setup API for state transitions.
options.AuthServer.ConfigureSsoPortal(portal =>
{
portal.UseHostedPortal = false;
portal.BuildUiUrl = ctx =>
$"https://admin.example.com/sso/setup?session_id={ctx.SessionId}&view={ctx.View}";
});Default setup API base path:
/sqlos/admin/auth/sso-portal/api/setupOverride it with SsoPortal.HeadlessApiBasePath. Keep it on the SqlOS origin or configure your host for credentialed browser requests, because the portal session is stored in an HttpOnly cookie.
The setup API returns SqlOSSsoSetupActionResult:
| Field | Description |
|---|---|
type | view or redirect |
redirectUrl | Optional browser redirect |
viewModel | Current setup state, service-provider values, provider guides, domain claim, latest test, and allowed actions |
State-machine endpoints:
| Method | Endpoint | Description |
|---|---|---|
| GET | / | Current setup view |
| PUT | /provider | Select SAML provider |
| PUT | /enrollment-policy | Update existing-member SSO and JIT provisioning policy |
| POST | /domain | Start DNS TXT domain verification |
| POST | /domains/{domainId}/confirm | Check the TXT record and activate the claim |
| POST | /metadata/validate | Validate SAML metadata XML |
| POST | /metadata | Import metadata without activating |
| POST | /activate | Activate when metadata and domain state allow it |
| POST | /disable | Disable the connection |
| POST | /organization-sessions/revoke | Confirm-gated revocation for active org sessions on the verified SSO domain |
| POST | /test | Record readiness or generate a test redirect |
| POST | /signout | Clear the portal session |
| Option | Default | Description |
|---|---|---|
autoProvisionUsers | false for portal-created connections | Create SqlOS users or memberships from SAML assertions on first login |
autoLinkByEmail | true for portal-created connections | Require existing verified org members to enroll and sign in through SSO |
SsoPortal.EnableApi | true | Expose portal and headless setup APIs |
SsoPortal.UseHostedPortal | true | Serve the bundled setup portal |
SsoPortal.BuildUiUrl | null | Redirect opened setup sessions to a host-owned UI |
SsoPortal.HeadlessApiBasePath | /sqlos/admin/auth/sso-portal/api/setup | Base path for the headless setup API |
SsoPortal.RequireVerifiedDomainForActivation | true | Require a verified domain before activating self-serve SSO |
SsoPortal.DomainVerificationRecordPrefix | _sqlos-verify | TXT record name prefix |
SsoPortal.DomainVerificationRecordValuePrefix | sqlos-domain-verification | TXT record value prefix |
SsoPortal.ReservedDomainRoots | empty | Domain roots customers cannot claim |
| Provider | SP Entity ID field | ACS field | Metadata source |
|---|---|---|---|
| Microsoft Entra | Identifier (Entity ID) | Reply URL | Federation Metadata XML |
| Okta | Audience URI (SP Entity ID) | Single sign-on URL | IdP metadata |
| Google Workspace | Entity ID | ACS URL | IdP metadata |
| Generic SAML | SP Entity ID | ACS URL | SAML metadata XML |
For metadata rotation, open the delegated portal again or use the dashboard, paste or upload the new XML, review the parsed IdP values, and activate. New metadata is validated before it replaces the active configuration.
@acme.com domain and checks the connection enrollment policy