Skip to content

Set up single sign-on (OIDC)

Point leancosts at your organization’s Keycloak — or any OIDC identity provider — so your team signs in with your IdP instead of magic-link email. SSO is configured per tenant, entirely through the app: no environment variables, no redeploy. It layers on top of magic-link, which stays as the bootstrap path until you choose to enforce SSO.

Everything below lives on Admin → Team → Single sign-on (/admin?tab=team&sub=sso) and requires a tenant admin (the governance.admin capability).

In your identity provider (Keycloak realm, Entra ID, Okta, Auth0, …), create a confidential OIDC client for leancosts:

  • Redirect / callback URI: https://app.leancosts.com/api/auth/sso/callback/sso_<your-workspace-slug> (your workspace slug is the …/t/<slug> segment of your sign-in URL). If your IdP enforces exact matching and rejects the sign-in later, copy the exact redirect_uri from that error and register it verbatim.
  • Scopes: openid email profile.
  • Client authentication: on (confidential) — you’ll get a client ID and client secret.
  • Make sure the realm’s discovery document ({issuer}/.well-known/openid-configuration) is publicly reachable from the internet — leancosts probes it directly.

Open Admin → Team → Single sign-on and fill in the form:

FieldWhat to enter
Display nameThe label on the sign-in button, e.g. Acme SSO.
Client IDThe OIDC client you registered for leancosts.
Issuer URLThe realm base URL, e.g. https://kc.acme.com/realms/acme. Discovery runs against {issuer}/.well-known/openid-configuration.
Client secretPaste the secret. It is write-only — stored encrypted and never shown again (you’ll see •••• and a Replace secret button on later edits).
ScopesOptional; defaults to openid email profile.
Require verified emailOn by default — rejects sign-ins where the IdP didn’t assert email_verified (account-takeover defense).

Click Connect realm. The realm is saved in status Draft.

Click Test connection. leancosts fetches your discovery document and reports Discovery OK (with the authorize endpoint) or an actionable error. A successful probe moves the realm to Tested.

Sign in once through the IdP at your workspace URL /t/<your-slug> and pick Continue with {your display name}. A successful round-trip flips the realm to Proven and stamps the time. Only a proven realm can be enforced.

By default, every new SSO user lands pending until an admin approves them. To auto-provision instead, expand Role mapping and turn on Map IdP groups to roles:

  • Group claim — the OIDC claim carrying the user’s groups (Keycloak’s default mapper emits groups).
  • Group → role mappings — map a group value (e.g. finops-admins) to a leancosts role. If a user is in several mapped groups, the highest-privilege match wins.

Save the mappings with Save changes (they save together with the realm).

Once the realm is Proven, the Require SSO toggle unlocks. Turning it on disables magic-link for everyone in your tenant — they must sign in through your IdP.

New SSO users (without a matching role mapping) arrive pending. Approve them on Admin → Team → Members — approving assigns the role you choose and grants access. Until then they can sign in but see no data.

  • “Could not reach the issuer” — the discovery URL isn’t publicly reachable, or the issuer is wrong. Confirm {issuer}/.well-known/openid-configuration loads from a browser.
  • Sign-in rejected with a redirect_uri error — register the exact callback URL from the error in your IdP client (see step 1).
  • Sign-in rejected for unverified email — the IdP didn’t assert email_verified. Verify the user’s email in your IdP, or (less safe) turn off Require verified email.
  • Can’t turn on Require SSO — the realm isn’t Proven yet. Complete one real SSO sign-in first.