Configure OIDC Single Sign-On
When you’d rather your operators sign in through an existing identity provider
(Keycloak, Okta, Auth0, Dex, …) than manage credentials in Cloacina, configure
the server as an OIDC relying party. A successful sign-in is mapped to one or
more {tenant, role} memberships by a god-owned allowlist and mints a
scoped bearer key per tenant — the same kind of key everything else uses.
OIDC is a server-only feature. For the no-IdP alternative, see Configure Local Accounts. For the trust model, see Security Model.
- A running
cloacina-serverand god (is_admin) access to set its configuration. - An OIDC provider with a registered confidential client. You’ll need its
issuer URL, a client id + client secret, and the ability to register
a redirect URI of
http(s)://<server>/v1/auth/callback. - The provider must expose standard discovery (
/.well-known/openid-configuration) and a JWKS endpoint — Cloacina uses theopenidconnectcrate’s spec-compliant discovery, PKCE, and ID-token validation.
Set these on the server (environment variables shown; absence of CLOACINA_OIDC_ISSUER
leaves OIDC disabled and the login routes unmounted):
| Variable | Purpose |
|---|---|
CLOACINA_OIDC_ISSUER |
The provider’s issuer URL (discovery is fetched from <issuer>/.well-known/openid-configuration). |
CLOACINA_OIDC_CLIENT_ID |
The registered client id. |
CLOACINA_OIDC_CLIENT_SECRET |
The client secret. |
CLOACINA_OIDC_REDIRECT_URI |
Must match a redirect URI registered with the provider, e.g. https://cloacina.example.com/v1/auth/callback. |
CLOACINA_OIDC_SCOPES |
Optional, comma-separated. Defaults to openid,email,profile,groups. |
CLOACINA_OIDC_MAP |
The identity→tenant allowlist (next section). |
CLOACINA_OIDC_SUCCESS_REDIRECT |
Where to send the browser after login — the web UI’s connect URL, e.g. https://cloacina.example.com/connect. |
The server runs discovery once at startup. If discovery fails (issuer
unreachable, misconfigured), OIDC is disabled rather than failing the whole
server — API-key and local-account auth keep working, and the OIDC routes return
501.
JWKS note: the server caches the provider’s signing keys at discovery. If your IdP rotates keys (or you recreate a test issuer), restart the server so it re-discovers.
CLOACINA_OIDC_MAP is a ;-separated, god-owned allowlist. Each rule is
<match>=<tenant>:<role>, where <match> is one of:
group:<name>— the identity is a member of this group (from thegroupsclaim),domain:<example.com>— the identity’s email is in this domain,sub:<subject>— an exact subject match.
<tenant> may be _ for a global principal; <role> is read / write / admin.
CLOACINA_OIDC_MAP="group:platform-admins=_:admin;group:acme-eng=acme:write;domain:acme.com=acme:read"
An identity matching no rule is denied (403). Rules are evaluated in order;
the first match per tenant wins, and an identity that matches rules for
several tenants is granted all of them (see Multi-tenant memberships).
Static-password IdP users (e.g. a Dex demo) often carry no
groupsclaim — map ondomain:in that case. Keepgroup:rules for real IdPs that emit groups.
- The user clicks SSO → Continue with SSO in the web UI (or navigates to
GET /v1/auth/oidc/login). The server redirects to the IdP with PKCE + state- nonce.
- The user authenticates at the IdP.
- The IdP redirects to
/v1/auth/callback. The server validates the ID token (JWKS signature,iss/aud/exp, nonce), resolves the identity through the allowlist, mints a scoped key per membership, and redirects toCLOACINA_OIDC_SUCCESS_REDIRECTwith the minted key(s) in the URL fragment (a fragment is never sent to a server or logged). - The SPA reads the fragment and lands the user authenticated, stripping the key from the URL.
In-flight login state (state / nonce / PKCE verifier) is persisted in Postgres,
so the callback can land on any replica — no sticky sessions.
A single sign-on can grant access to several tenants at once. When the allowlist matches an identity to more than one tenant, the server mints one key per tenant and the UI presents a tenant picker — the user chooses where to start, and the others are one click away in the tenant switcher. A single-tenant identity skips the picker and lands straight in.
The bearer key is always scoped to exactly one tenant; “multi-tenant” lives in holding several scoped keys and switching, not in a multi-tenant subject. See the Security Model.
The repository’s demo stack ships a self-contained Dex issuer wired to the
server — see docker/AUTH_DEMO.md for an end-to-end walkthrough (the one
/etc/hosts line a browser needs, the demo allowlist, and the multi-tenant
picker).
- Configure Local Accounts — the no-IdP alternative.
- Security Model — ABAC authorization, identity providers, session lifecycle.
- Manage API Keys — directly-created keys.