SSO identity provider guides
Audience: identity provider (IdP) administrators choosing and wiring a provider (Entra ID, Okta, Keycloak, ...) for TokenTimer Enterprise.
TokenTimer Enterprise supports multiple OpenID Connect and SAML 2.0 providers
side by side. In the dashboard, open System Settings → Manage SSO providers
(the UI may label this SSO Providers). Each provider has a stable slug
(for example entra, keycloak, okta). Use a short IdP name; do not put -oidc or -saml in the
slug (the type field already selects the protocol). When one vendor runs both
protocols, use two distinct slugs (e.g. keycloak and keycloak2).
The login page shows one button per enabled, entitled provider
(GET /api/auth/features).
Quickstarts:
Sign-in flow at a glance
PKCE (Proof Key for Code Exchange) is an OIDC hardening step, enabled by default.
Provider guides
| Provider | Protocols | Guide |
|---|---|---|
| Keycloak | OIDC (SAML optional) | Keycloak |
| Microsoft Entra ID | OIDC, SAML | Microsoft Entra ID |
| Okta | OIDC, SAML | Okta |
| Google Workspace | OIDC | Google Workspace |
| Auth0 | OIDC | Auth0 |
| Amazon Cognito | OIDC | Amazon Cognito |
| Active Directory Federation Services (AD FS) | SAML | AD FS |
Shared references:
- Claim mapping: claim path cheat-sheet
- Group mappings: map IdP groups to roles
- Configuration reference: bootstrap env vars and global SSO settings
- Authentication model: bootstrap and account linking
Configuration surfaces
| Surface | Use for |
|---|---|
| System Settings → Manage SSO providers | Create, edit, test, and enable providers; copy slug-scoped callback URLs |
| System Settings → Single Sign-On | IdP group to TokenTimer role bindings |
Helm ssoProviders[] | GitOps bootstrap; chart renders sso-providers.json ConfigMap |
Compose sso-providers.json | Local/bootstrap manifest (bind-mounted at /etc/tokentimer/sso-providers.json) |
SSO_<SLUG>_* env vars | Secret material and GitOps field locks per provider (see Configuration reference) |
Prerequisites (all providers)
| Requirement | Notes |
|---|---|
| Enterprise license | feature:oidc and/or feature:saml on the JWT |
| Public API URL | Must match what browsers and the IdP use for callbacks (API_URL) |
| HTTPS in production | Session cookies expect TLS (APP_URL, API_URL) |
TRUST_PROXY_HOPS | Match your ingress/LB chain (default 2). Wrong values break callback URLs behind TLS |
| Ingress | Route /auth/* to the API service, not only /api/* |
| Secrets keyring | An encryption keyring for SSO secrets. Helm auto-creates <release>-enterprise-secrets by default; Compose uses SSO_SECRETS_KEYRING_FILE. To supply your own, follow the Keyring JSON format section in the Configuration reference |
URLs TokenTimer uses
Replace https://tokentimer.example.com with your deployment (API_URL) and
<slug> with the provider slug you chose under Manage SSO providers or in the bootstrap
manifest.
| Purpose | OIDC | SAML |
|---|---|---|
| Browser sign-in start | GET /auth/oidc/<slug> | GET /auth/saml/<slug> |
| Callback (register in IdP) | GET /auth/oidc/<slug>/callback | POST /auth/saml/<slug>/callback (the ACS URL, see below) |
| Default callback if unset | {API_URL}/auth/oidc/<slug>/callback | {API_URL}/auth/saml/<slug>/callback |
| SP metadata (export to IdP) | n/a | GET {API_URL}/auth/saml/<slug>/metadata |
SAML terminology: the ACS (Assertion Consumer Service) URL is the address your IdP posts the SAML response to, and the SP (service provider) is TokenTimer's role in the SAML exchange.
API_URL / apiUrl must be the API origin
API_URL (Helm: tokentimer.config.apiUrl) is the API host origin, e.g.
https://tokentimer.example.com. The dashboard and server append /api for
JSON routes (e.g. /api/auth/features) and /auth for SSO routes themselves.
| Setting | Value |
|---|---|
API_URL env / tokentimer.config.apiUrl | https://tokentimer.example.com |
APP_URL env / tokentimer.config.baseUrl | https://tokentimer.example.com |
| IdP redirect URI (OIDC) | https://tokentimer.example.com/auth/oidc/<slug>/callback |
| IdP ACS (SAML) | https://tokentimer.example.com/auth/saml/<slug>/callback |
Split host (dashboard and API on different origins):
| Setting | Example |
|---|---|
tokentimer.config.baseUrl / APP_URL | https://app.example.com |
tokentimer.config.apiUrl / API_URL | https://api.example.com |
SESSION_COOKIE_DOMAIN | .example.com |
| IdP redirect URI (OIDC) | https://api.example.com/auth/oidc/<slug>/callback |
Ingress routing for that origin:
| Path prefix | Service |
|---|---|
/api/* | API |
/auth/* | API |
/ | dashboard |
Deep links: /auth/oidc/<slug>?next=/dashboard/... and
/auth/saml/<slug>?next=/dashboard/... (OIDC stores next on the session;
SAML sends it as RelayState. Same-origin pathname only, max 512 chars; see
Claim mapping).
Choosing OIDC vs SAML
| Prefer OIDC when | Prefer SAML when |
|---|---|
| IdP supports OIDC well (most cloud IdPs) | Corporate standard is SAML-only |
| You want discovery defaults and minimal claim plumbing | Existing enterprise SAML app template |
| Easier group claims in JWT/userinfo | AD FS or legacy apps without OIDC |
You may register both protocols as separate providers (different slugs) when the license includes both entitlements.
TokenTimer reads groups from configurable claim/attribute paths for role mapping. If the IdP cannot emit groups in the token, use bootstrap admin group mappings in the provider row or fix IdP token configuration.
Behavior reference (advanced)
How the SSO surface maps to the implementation. You do not need this table for a standard setup; come back to it when debugging or automating.
| Topic | Behaviour |
|---|---|
| Provider registry | Rows in sso_providers and encrypted secrets in sso_provider_secrets |
| Sign-in readiness | /api/auth/features exposes per-provider signInReady. Login buttons use signInReady only. Status badges under Manage SSO providers match this. |
| HA config sync | UI or API writes to sso_providers bump sso_config_version; all API pods reload within SSO_CONFIG_SYNC_INTERVAL_MS (default 45s) or on the next slugged /auth/* request. Bootstrap manifest changes require a pod restart. |
| Callback URLs | Derived from slug + API_URL unless config.callback_url is set on the provider row. Manage SSO providers shows the computed URL with copy-to-clipboard. |
| OIDC discovery | Fetched from ${issuer}/.well-known/openid-configuration (5 min in-process cache per pod). Skip per provider with security.disable_discovery. Keycloak realm issuers get /protocol/openid-connect/* fallbacks when discovery fails. |
| OIDC diagnostics | Admins: Test on the provider under Manage SSO providers, or POST /api/admin/sso/providers/<slug>/test. |
| PKCE | Authorization Code + PKCE S256 when security.pkce is true (default). OIDC session state is in Postgres, not pod memory. |
| HTTPS enforcement | In production (NODE_ENV=production) the OIDC strategy refuses to register if any of issuer / authorization / token / userinfo / JWKS URLs are http://. http://localhost is allowed outside production. |
| SAML InResponseTo | Defaults to always. Postgres table saml_request_cache backs the cache across replicas. Override policy with security.validate_in_response_to=ifPresent or never only when the IdP cannot echo InResponseTo. |
| Default scopes | openid profile email unless config.scopes overrides. |
| Default claim paths | See Claim mapping. |
| New users | Provisioning requires an email claim/attribute on first login; subject-only works for users already linked in external_identities. |
| License gate | /auth/oidc/<slug>* requires feature:oidc; /auth/saml/<slug>* requires feature:saml. |
| Ingress | Route /auth/* to the API service (CSRF is exempt for these paths). |
Full bootstrap and global env vars: Configuration reference.
Official documentation (starting points)
| Provider | OIDC | SAML |
|---|---|---|
| Microsoft Entra | Register an app, Redirect URI, ID token claims | Enable SAML SSO |
| Okta | Auth code flow, Create app integration | SAML app integration |
| OpenID Connect | N/A (use OIDC or a third-party SAML bridge) | |
| Auth0 | Regular Web App, OIDC | SAML SP |
| AWS Cognito | User pool as OIDC IdP, App client settings | SAML IdP (Cognito as SP; TokenTimer is SP, so use user pool OIDC) |
| Keycloak | Securing apps | SAML clients |
| AD FS | N/A | Configure RP |