Skip to main content

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

  1. In Okta, create an OIDC - OpenID Connect Web Application.
  2. Set its sign-in redirect URI to exactly:
https://synth.example.com/api/auth/oidc/callback
  1. Assign the people or groups that may use Synth.
  2. 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.
  3. 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.

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

  1. Confirm the Business license reports the SSO entitlement.
  2. Confirm GET /api/auth/oidc/status returns configured: true and entitled: true.
  3. Use a non-admin Okta test user and confirm the expected Synth access.
  4. Use a member of synth-admins and confirm the group claim grants the intended admin mapping.
  5. Confirm an unassigned or disabled Okta user receives no Synth session.
  6. 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.