SSO RBAC Enablement Guide
Ship-blocking runbook for enabling OIDC SSO RBAC enforcement (OIDC_SSO_RBAC_ENABLED=true) safely. Covers the soft-lockout hazard, preconditions, the enablement sequence, and rollback.
SSO RBAC Enablement Guide
Tracked by: #1821 (Definition of Done)
Spec: ADR-021 Phase 2 — docs/superpowers/specs/2026-07-02-adr021-phase2-sso-design.md
This guide is SHIP-BLOCKING. Do not set
OIDC_SSO_RBAC_ENABLED=truein any environment before completing every step below. Skipping the preconditions can lock out all SSO users until corrected.
Primary operational hazard — soft-lockout
Scenario: An operator sets OIDC_SSO_RBAC_ENABLED=true before configuring
any group mappings. Effect: every SSO login receives a zero-binding session.
With the default no_match_posture = zero_binding_session the user is
authenticated but denied access to every resource. With
no_match_posture = deny_session the UI login ceremony returns 403 no_access
immediately.
In both cases the operator (who may also be an SSO-only user) can be locked
out. The only escape is either restoring the flag to false and redeploying, or
performing an out-of-band admin action with a temp_admin JWT.
The SsoEnablementWarning banner (shown in the UI when the flag is true and
zero mappings are configured) is an advisory mitigation for this hazard — not a
hard gate. An already-locked-out admin who 403s on the mappings endpoint will
not see the banner. The real safeguards are the preconditions below.
Preconditions (complete ALL before enabling)
P1 — Group mappings configured and preview-validated
- Sign in as an admin and navigate to Settings → SSO Group Mappings.
- Add at least one mapping that covers your primary admin group.
- Use the Preview Claim tool to paste an example token payload and confirm
the expected mappings match. Check for:
- Case-sensitivity mismatches (e.g.
Adminsvsadmins). - Dotted-path issues (e.g.
realm_access.rolesresolves correctly). - Dead-scope references (cluster-scoped mappings whose cluster no longer exists).
- Case-sensitivity mismatches (e.g.
P2 — Keycloak access-token TTL ≤ 5 minutes (E-OQ-F)
The staleness window for group changes is bounded by the OIDC access-token lifetime. A token with a 24 h lifetime means a user removed from a group retains their bindings until the token expires — up to 24 h.
Before enabling enforcement:
-
Open the Keycloak admin console.
-
Navigate to Realm Settings → Tokens → Access Token Lifespan.
-
Set it to 300 seconds (5 minutes) or less.
Alternatively set it per-client:
- Open the target client → Advanced tab → Access Token Lifespan.
- Set the override to 300 s or less.
-
Confirm the change takes effect by issuing a new token and checking
exp - iat.
Note: The PodWarden frontend cannot read Keycloak's realm-level TTL config, so there is no automatic validation of this precondition. The SsoEnablementWarning banner will prompt you, but the TTL confirmation is entirely your responsibility.
P3 — Legacy-admin first-login path understood
Existing manually-created admin accounts have oidc_subject = NULL. Their first
SSO login hits the identity_conflict path (same verified email, no sub match).
An admin must perform the one-time explicit safe-link action (Settings → Users →
Link SSO Identity) before that account can use SSO login.
Plan who will do this link and test it in staging before production enablement.
The temp_admin JWT can perform the link if the only admin would otherwise be
locked out.
Enablement sequence
Step 1 — Staging validation
-
Confirm all preconditions (P1, P2, P3) in staging.
-
Set
OIDC_SSO_RBAC_ENABLED=trueon both containers:- API container:
OIDC_SSO_RBAC_ENABLED=true - Frontend container:
OIDC_SSO_RBAC_ENABLED=true
Use the plain
OIDC_SSO_RBAC_ENABLEDform on the frontend container — it is the single supported way. The frontend runtime resolver (readRuntimeEnv) also accepts theNEXT_PUBLIC_OIDC_SSO_RBAC_ENABLEDalias, but setting both spellings to different values will behave unexpectedly. Always set the plain form; do not set both. - API container:
-
Redeploy the stack.
-
Verify the SsoEnablementWarning banner does not appear (mappings configured).
-
Test SSO login with a user whose IdP groups match a mapping. Confirm:
- The callback reaches
/(or the intended callbackUrl) without error. - The backend
/api/v1/auth/sso-sessionreturnssession_type: "bound". - The user has the expected role bindings (visible in Settings → Users).
- The callback reaches
-
Test SSO login with a user whose IdP groups do not match any mapping. Confirm:
- The
/auth/sso-session?outcome=zero_bindingpage appears. - The user is logged in but gets 403 on protected endpoints.
- The
-
Test with a suspended user. Confirm
403 account_suspendederror page.
Step 2 — Production enablement
Only after Step 1 is clean:
- Set
OIDC_SSO_RBAC_ENABLED=trueon both containers in production (plain form — see note in Step 1). - Redeploy.
- Verify the SsoEnablementWarning banner is absent.
- Smoke-test SSO login.
- Monitor the
/api/v1/auth/sso-sessionresponse metrics for unexpected 403/422 rates.
Rollback
Primary (data-preserving, instant): Set OIDC_SSO_RBAC_ENABLED=false on
both containers and redeploy. The legacy hasRequiredGroup gate is restored.
No database changes are required.
Data rollback (if needed after significant SSO usage):
-- Remove all SSO-managed role bindings.
DELETE FROM user_roles WHERE is_sso_managed = true;
-- Remove SSO-provisioned users (those created by the SSO path, not manually).
-- WARNING: verify before running in production.
UPDATE system_users SET provisioned_via = NULL, oidc_issuer = NULL,
oidc_subject = NULL WHERE provisioned_via = 'sso';Audit rows are retained by design.
Monitoring signals
| Signal | Meaning |
|---|---|
sso_session 403 no_access in logs | User's IdP groups have no mapping match. |
sso_session 403 account_suspended | Suspended user attempted SSO login. |
sso_session 403 identity_conflict | Pre-existing account with same verified email; admin link required. |
sso_session 422 claim_unparseable | IdP token's groups claim is malformed or at wrong path. Check groups_claim config. |
| SsoEnablementWarning banner visible | Enforcement enabled but zero mappings — all SSO logins will produce zero-binding sessions. |
Related pages
- Settings → SSO Group Mappings — configure mappings and preview claims
- Settings → System Config —
oidc_ssoJSONB object reference - Login — SSO session outcome display (zero_binding state)
- ADR-021 RBAC v1.1 — governing architecture decision