SSO / OIDC
Synth includes OIDC routes for company login. Treat SSO as a Business deployment feature, not something you need for the first local run.
When To Use It
Use SSO when:
- a team shares one Synth instance
- users should sign in with Okta, Google Workspace, Azure/Entra, or Keycloak
- access should follow company identity policy
Okta Setup
- In Okta, create an OIDC - OpenID Connect Web Application.
- Set its sign-in redirect URI to exactly:
https://synth.example.com/api/auth/oidc/callback
- Assign the people or groups that may use Synth.
- Copy the issuer URL, client ID, and client secret. For an Okta custom
authorization server, the issuer commonly ends in
/oauth2/default; use the exact issuer shown by Okta. - Choose either the UI path or the Helm + CaSc path below.
The issuer must expose a public HTTPS discovery document at
<issuer>/.well-known/openid-configuration.
Helm + CaSc (Recommended For Production)
Create the client-secret Kubernetes Secret outside Git:
kubectl -n synth create secret generic synth-okta \
--from-literal=client-secret="$OKTA_CLIENT_SECRET" \
--dry-run=client -o yaml | kubectl apply -f -
Then commit only the non-secret configuration:
synth:
publicUrl: https://synth.example.com
sso:
enabled: true
issuerUrl: https://customer.okta.com/oauth2/default
clientId: 0oa-customer-synth
existingSecret: synth-okta
existingSecretKey: client-secret
redirectUri: https://synth.example.com/api/auth/oidc/callback
scopes: [openid, profile, email, groups]
autoProvision: true
defaultRole: member
adminClaim: groups
adminGroupValue: synth-admins
Helm renders the non-secret fields into CaSc and injects the secret as
SYNTH_OIDC_CLIENT_SECRET. If that environment variable is missing, the CaSc
import rejects the SSO configuration and removes any stale persisted OIDC
configuration; the raw secret is never written into the CaSc ConfigMap. The
pod can still start for local-admin recovery, but SSO will not be configured.
If you supply a complete synth.casc.raw document instead of synth.sso, put
SYNTH_OIDC_CLIENT_SECRET in the Secret referenced by
secrets.existingSecret, and use this CaSc shape:
global_settings:
sso:
enabled: true
issuer_url: https://customer.okta.com/oauth2/default
client_id: 0oa-customer-synth
client_secret:
env: SYNTH_OIDC_CLIENT_SECRET
redirect_uri: https://synth.example.com/api/auth/oidc/callback
scopes: [openid, profile, email, groups]
auto_provision: true
default_role: member
admin_claim: groups
admin_group_value: synth-admins
Global Settings UI
In Global Settings -> SSO, enter the issuer, client ID, client secret, redirect URI, scopes, default role, and optional admin group mapping. Save, then use Test SSO Login before inviting users.
Production Verification
- Confirm the Business license reports the SSO entitlement.
- Confirm
GET /api/auth/oidc/statusreturnsconfigured: trueandentitled: true. - Use a non-admin Okta test user and confirm the expected Synth access.
- Use a member of
synth-adminsand confirm the group claim grants the intended admin mapping. - Confirm an unassigned or disabled Okta user receives no Synth session.
- Restart the pod and repeat login to prove configuration persistence.
The repository harness validates rendering, secret isolation, PKCE/state, SSRF protection, license gating, and user guards without contacting the customer tenant:
bash harness/scripts/harness_docker.sh --harness okta-casc --runs 1 --require-report
Passing this harness does not replace the interactive tenant test.
Local First
For local development, use the built-in username/password setup. Add SSO only after Projects and MCP retrieval are working. Provider setup can remain separate unless the deployment also needs model-backed jobs.