SSO RBAC Enablement Guide
Retired runbook — the SSO group-to-role binding feature it switches on has been removed from the product. See the banner for what OIDC_SSO_RBAC_ENABLED actually does today.
SSO RBAC Enablement Guide
This runbook describes a feature that has been removed. The group-to-role binding machinery it exists to switch on — the oidc_group_mappings and user_roles tables, the group-claim reconcile, the Settings → SSO Group Mappings panel and its Preview Claim tool — was deleted from the product, and the tables are dropped by migration. Precondition P1 below cannot be carried out: there is no mappings panel, no preview tool, and nowhere for a mapping to be stored.
What OIDC_SSO_RBAC_ENABLED does today. The environment variable still exists and still defaults to false. Setting it to true no longer enables any authorization enforcement — it enables OIDC identity provisioning: on first SSO login PodWarden creates a system_users row for the principal, storing the role readonly, and links the OIDC subject to it. Group claims influence nothing.
Enabling it does not restrict anyone. PodWarden Core's authorization model is flat — every authenticated caller has the same access, secret values included, and the stored readonly role is not enforced. Enabling the flag therefore gives every SSO user who signs in full administrative access to the instance, which is the opposite of what the sections below imply. Control who may reach the instance at your identity provider, not here.
The soft-lockout hazard described below does not arise as written, because there are no bindings left to be missing. Precondition P3 does still apply: an existing, manually-created admin whose oidc_subject is NULL still hits the identity_conflict path on first SSO login, and still needs the one-time safe-link action (POST /api/v1/users/{user_id}/link-sso-identity).
This page is kept while the documentation is corrected. Treat every section below as describing a feature that no longer exists, except where noted above.
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)