Files
stack/docs/ADMIN-GUIDE/security/sso-providers.md
T

8.4 KiB

title, type, audience, status, source_of_truth
title type audience status source_of_truth
SSO Providers runbook admin current false

SSO Providers

Configure optional enterprise single sign-on for Mosaic Stack through Better Auth's generic OAuth integration. The gateway owns provider configuration and discovery; the web application renders only providers reported by the gateway.

Current behavior: Authentik, WorkOS, and Keycloak are supported in the checked-in implementation. Authentik and WorkOS use OIDC. Keycloak supports OIDC and an optional direct SAML login URL. The web application does not read NEXT_PUBLIC_WORKOS_ENABLED or NEXT_PUBLIC_KEYCLOAK_ENABLED; provider buttons are discovered dynamically from the gateway.

Prerequisites

Before configuring a provider, establish these gateway settings:

  • BETTER_AUTH_URL — the public base URL used to construct OAuth callback URLs.
  • BETTER_AUTH_SECRET — a strong secret for Better Auth sessions and tokens.
  • GATEWAY_CORS_ORIGIN — the web origin or comma-separated origins allowed by the gateway.

Keep client secrets and Better Auth secrets in the deployment secret store. Do not commit them to .env files or expose them to the web bundle.

Provider configuration

Authentik OIDC

Set all three variables to enable Authentik:

AUTHENTIK_ISSUER=https://auth.example.com/application/o/mosaic
AUTHENTIK_CLIENT_ID=...
AUTHENTIK_CLIENT_SECRET=...

The implementation derives OIDC discovery and endpoint URLs from AUTHENTIK_ISSUER. An optional team-claim label can be exposed in provider discovery:

AUTHENTIK_TEAM_SYNC_CLAIM=groups

The default reported claim is groups. The discovery payload reports this claim; verify any downstream membership-sync behavior separately before treating it as an authorization guarantee.

Register this redirect URI with the Authentik application:

{BETTER_AUTH_URL}/api/auth/oauth2/callback/authentik

WorkOS OIDC

Set all three variables to enable WorkOS:

WORKOS_ISSUER=https://your-company.authkit.app
WORKOS_CLIENT_ID=client_...
WORKOS_CLIENT_SECRET=...

Use the WorkOS AuthKit issuer or custom authentication domain, not a raw WorkOS REST API hostname. Mosaic derives the OIDC discovery URL by appending /.well-known/openid-configuration to the issuer. WorkOS uses PKCE and issuer validation in the current auth configuration.

An optional team-claim label can be exposed in provider discovery:

WORKOS_TEAM_SYNC_CLAIM=organization_id

The default reported claim is organization_id. The discovery payload reports this claim; verify any downstream membership-sync behavior separately before treating it as an authorization guarantee.

Register this redirect URI with the WorkOS application:

{BETTER_AUTH_URL}/api/auth/oauth2/callback/workos

Keycloak OIDC

Use either an explicit issuer or the URL-plus-realm form. The client ID and secret are required in both forms.

Explicit issuer:

KEYCLOAK_ISSUER=https://auth.example.com/realms/mosaic
KEYCLOAK_CLIENT_ID=mosaic
KEYCLOAK_CLIENT_SECRET=...

Derived issuer:

KEYCLOAK_URL=https://auth.example.com
KEYCLOAK_REALM=mosaic
KEYCLOAK_CLIENT_ID=mosaic
KEYCLOAK_CLIENT_SECRET=...

KEYCLOAK_ISSUER takes precedence when both forms are present. Keycloak uses PKCE and issuer validation in the current auth configuration.

An optional team-claim label can be exposed in provider discovery:

KEYCLOAK_TEAM_SYNC_CLAIM=groups

The default reported claim is groups. The discovery payload reports this claim; verify any downstream membership-sync behavior separately before treating it as an authorization guarantee.

Register this redirect URI with the Keycloak client:

{BETTER_AUTH_URL}/api/auth/oauth2/callback/keycloak

Keycloak direct SAML fallback

The current web flow supports a direct SAML link for Keycloak when KEYCLOAK_SAML_LOGIN_URL is configured:

KEYCLOAK_SAML_LOGIN_URL=https://auth.example.com/realms/mosaic/protocol/saml

This creates a configured Keycloak provider with loginMode: saml. The web login button links directly to the supplied URL as Continue with Keycloak (SAML); there is no Better Auth OIDC callback for this mode. The URL must be the provider's valid SAML launch URL for the deployment.

A SAML-only Keycloak configuration does not require the Keycloak OIDC client variables. Do not combine an incomplete OIDC variable set with SAML-only configuration: partial OIDC configuration is rejected during auth setup.

Callback and discovery contract

Better Auth is mounted at /api/auth. OIDC callbacks use:

{BETTER_AUTH_URL}/api/auth/oauth2/callback/{providerId}

The gateway exposes provider discovery at:

GET /api/sso/providers

The response includes authentik, workos, and keycloak records with:

  • configured — whether a usable OIDC or Keycloak SAML configuration is present.
  • protocols — supported protocols for the provider record.
  • loginModeoidc, saml, or null.
  • callbackPath — the OIDC callback path, or null for SAML-only mode.
  • teamSync — the configured/default claim label exposed to the UI.
  • samlFallback — whether a direct Keycloak SAML URL is configured.
  • warnings — partial OIDC configuration warnings when reported.

The web login page filters this response to configured providers. OIDC buttons use Better Auth's signIn.oauth2 flow. A configured Keycloak SAML fallback is rendered as a direct link. No provider-specific NEXT_PUBLIC_*_ENABLED flag is required or consumed.

Configuration procedure

  1. Set BETTER_AUTH_URL to the public gateway URL that the identity provider can reach.

  2. Set BETTER_AUTH_SECRET and the correct GATEWAY_CORS_ORIGIN values.

  3. Choose one provider configuration above and set its complete required variable group.

  4. Register the exact OIDC callback URI with the identity provider, when using OIDC.

  5. Restart or redeploy the gateway so it loads the changed environment.

  6. Inspect discovery without exposing secrets:

    curl "$BETTER_AUTH_URL/api/sso/providers"
    
  7. Open the web login page and confirm that only configured providers are shown.

  8. Complete a sign-in and verify the callback returns to the configured application.

Partial configuration and failure modes

Provider configuration is optional. If no provider variables are set, the gateway can run without SSO providers and the web login page renders no SSO section.

For Authentik and WorkOS, setting only part of the issuer/client ID/client secret group raises a configuration error. For Keycloak OIDC, the client ID, client secret, and either an explicit issuer or a complete URL-plus-realm pair are required. Empty or whitespace-only values are treated as unset.

Common failures:

Symptom Check
Provider does not appear on the login page Query /api/sso/providers; verify the complete provider variable group is present in the gateway environment.
OAuth callback is rejected Compare the registered redirect URI character-for-character with BETTER_AUTH_URL and the provider callback path.
Provider is shown but sign-in cannot start Check loginMode, issuer discovery, client credentials, and gateway logs.
Keycloak SAML button is absent Set KEYCLOAK_SAML_LOGIN_URL to the provider's direct launch URL and reload the gateway.
Startup/auth initialization reports missing variables Remove the partial provider configuration or provide the complete required group.
SSO succeeds but team membership is unexpected Treat teamSync.claim as discovery metadata and verify the actual claim mapping and membership-sync implementation.

Do not enable a provider by adding the obsolete NEXT_PUBLIC_WORKOS_ENABLED or NEXT_PUBLIC_KEYCLOAK_ENABLED variables. They are not read by the current web application.