feat(oidc): manage the admin flag from a configurable id_token claim - #2576
feat(oidc): manage the admin flag from a configurable id_token claim#2576btallman wants to merge 1 commit into
Conversation
Admins under OIDC/SSO previously came only from the file-backed admin list. Now the IdP can be the source of truth: when OMNIGENT_OIDC_ADMIN_CLAIM is set, that id_token claim is authoritative for users.is_admin at every login — a matching OMNIGENT_OIDC_ADMIN_VALUE promotes, a present-but-unmatching claim demotes. - Claim shapes: list (groups/roles — any element matches), scalar string (exact match), boolean (the bool itself; value optional). - Fail-safe: a token without the claim (or a null/undecidable shape) is a no-op, so an IdP that stops emitting the claim can never mass-demote existing admins. - The file-backed admin list (promote_if_listed) still runs after the sync as a break-glass, promote-only override. To read the claims, the callback threads the validated id_token out of the signature/iss/aud check (_resolve_oidc_email -> _resolve_oidc_identity), so the sync only ever sees IdP-signed data. GitHub OAuth (no id_token) is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@btallman This PR is a Bug fix, Feature, or UI / frontend change but the Demo section is missing or only contains a placeholder. These change types require a screenshot or screen recording so reviewers can see the new behaviour without checking out the branch. Please update the Demo section with:
Use |
|
Thanks — the overall IdP-driven admin design looks good. Before we merge, could you please make the claim-shape handling strict? Accepted shapes should be:
For any other shape — including objects, numbers, or lists containing non-string elements — please log a warning and return Please add regression tests for malformed scalar values and mixed/non-string lists. Also rebase the PR onto the current master once this is addressed. |
|
Closing this PR because it has been labeled The label was last applied on 2026-07-17T05:14:22Z. If you are ready to continue, please reopen this PR or open a new one. |
Related issue
Closes #2577. Builds on #1846 (OIDC admin surface) and #1859 (
email_verifiedopt-out); originally raised in #1489 (closed).Summary
OMNIGENT_OIDC_ADMIN_CLAIM(+ optionalOMNIGENT_OIDC_ADMIN_VALUE) and thatid_tokenclaim becomes authoritative forusers.is_adminat every login — matching users are promoted, losing the claim demotes them.ELI5: put someone in your
omnigent-adminsIdP group and they become an Omnigent admin next login; remove them and they stop being one. A local admins file stays as a spare key so you can't lock yourself out.flowchart LR A[id_token verified<br/>sig + iss + aud] --> B{ADMIN_CLAIM set?} B -- no --> D[promote_if_listed<br/>break-glass file] B -- yes --> C[evaluate claim] C -- match --> P[is_admin = true] C -- present, no match --> M[is_admin = false] C -- absent / null --> N[no-op] P --> D M --> D N --> DThe claim name and value are both env-driven (nothing IdP-specific is hardcoded): JumpCloud uses
memberOf, Okta/Entra/Google typicallygroups, othersroles.Test Plan
pytest tests/server/test_admin_list.py tests/server/test_oidc_callback.py tests/server/test_oidc_open_redirect.py— 69 passed on a cleanmaincheckout.id_tokens through the production decode path (promote / demote / absent-claim no-op / admin-list break-glass / feature-off inert), plus a verdict-table +sync_admin_claimunit tests.ruff format --check+ruff checkclean on all touched files.memberOf, a login carryingmemberOf=['omnigent-admins', …]promoted a user who was not in the admin file (pure claim path); server loggedadmin claim 'memberOf' promoted <user>and the DB flag flipped.Demo
N/A— no UI change (env-configured server behaviour).Type of change
Test coverage
Coverage notes
Manually verified end-to-end against a live JumpCloud OIDC deployment (see Test Plan): confirmed the claim promotes a non-file user, and that
memberOfmust be emitted in theid_token(the server reads decodedid_tokenclaims, not UserInfo) and only carries app-bound group names.Changelog
Manage OIDC/SSO admins from an
id_tokengroup/role claim viaOMNIGENT_OIDC_ADMIN_CLAIM