From 4b645f4dd2075af07cf7f3be86f9fc4d1cf1cb83 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Tue, 7 Jul 2026 21:09:13 +0100 Subject: [PATCH] Add OIDC scope group auth fix spec --- docs/OIDC_SCOPE_GROUP_AUTH_FIX_SPEC.md | 149 +++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 docs/OIDC_SCOPE_GROUP_AUTH_FIX_SPEC.md diff --git a/docs/OIDC_SCOPE_GROUP_AUTH_FIX_SPEC.md b/docs/OIDC_SCOPE_GROUP_AUTH_FIX_SPEC.md new file mode 100644 index 000000000..0e32e1881 --- /dev/null +++ b/docs/OIDC_SCOPE_GROUP_AUTH_FIX_SPEC.md @@ -0,0 +1,149 @@ +# OIDC Scope And Group Authorization Fix Spec + +## Status + +Active v6.0.5 RC regression. + +Primary issue: #1535 + +Related issues: #1528, #1533 + +Governed owners: + +- `api-contracts`: SSO provider API payloads, OIDC login initialization, callback authorization, session identity. +- `frontend-primitives`: Settings -> Security -> Single Sign-On provider configuration. + +This document is a handoff spec. It is not a solution design. + +## User-Visible Failure + +Settings-configured OIDC can complete part of the login flow, but group-based authorization and role mapping still fail for reporters using v6.0.4 through v6.0.5-rc.3. + +The visible failure reported on #1535 after v6.0.5-rc.3: + +- the displayed username/session label is no longer the internal `sso:oidc:...` principal +- group authorization still fails with `Your account is not part of an authorized group to use Pulse.` +- the observed OIDC authorization request scope is only `openid profile email` +- the reporter expects a configured group claim to be available for group role mapping + +Reported IdPs: + +- Pocket ID +- Authentik + +## What Is Already Fixed + +The following commits are present on `origin/main` and in the v6.0.5 RC line: + +- `caa9b41834` `Fix OIDC provider detail persistence` + - Fixes SSO provider API/detail persistence for nested OIDC fields, groups claim, allowed groups, and group role mappings when the payload supplies them. + - References #1521. +- `1c8a9346ef` `Fix legacy OIDC SSO discovery and CSP nonce` + - Restores the saved/legacy OIDC discovery path and SSO button behavior. + - References #1533. +- `eb99d7a6b3` `Fix SSO session display labels` + - Keeps the provider-scoped SSO principal as the stable session owner while displaying the IdP username/email/display claim in app chrome. + - References #1535. + +These commits do not finish group-scope authorization for Settings-configured OIDC. + +## Current Evidence + +Current `origin/main` evidence: + +- `internal/api/identity_sso_handlers.go` + - OIDC provider detail responses expose nested OIDC scopes. + - Create/update handlers can persist supplied OIDC scopes, groups claim, allowed groups, and group role mappings. +- `internal/api/sso_handlers_crud_test.go` + - API tests prove a payload containing `["openid", "profile", "email", "groups"]` can round-trip through provider detail and persistence. +- `frontend-modern/src/components/Settings/ssoProvidersModel.ts` + - The form model has `oidcScopes`. + - The empty form default is `openid profile email`. + - The payload builder sends `oidc.scopes` from `form.oidcScopes`. +- `frontend-modern/src/components/Settings/SSOProvidersPanel.tsx` + - The OIDC create/edit UI exposes issuer, client, secret, redirect/logout, groups claim, allowed groups, allowed domains, allowed emails, and group role mappings. + - The OIDC create/edit UI does not render an editable OIDC scopes field. +- `internal/api/oidc_handlers.go` + - Login initialization falls back to `openid profile email` when provider scopes are empty. + - Group restriction and group role mapping depend on the configured groups claim being present in the OIDC claims. + - Group claim extraction already accepts arrays and comma/space-separated strings. + +Commit history evidence: + +- No commits from `v6.0.0..origin/main` touch `frontend-modern/src/components/Settings/SSOProvidersPanel.tsx`. +- No commits from `v6.0.0..origin/main` touch `frontend-modern/src/components/Settings/ssoProvidersModel.ts`. + +## Expected Product Behavior + +An administrator configuring OIDC through Settings must be able to view and edit the exact OIDC scopes Pulse uses for the authorization request. + +The configured scopes must be the same scopes that: + +- are saved by provider create/update +- are returned by provider detail +- are shown again when the provider is reopened for editing +- are used when Pulse builds the OIDC authorization request + +For providers with no custom scopes configured, existing behavior remains: + +- default scopes are `openid profile email` +- existing providers continue to work without requiring manual reconfiguration + +For providers that require an extra group scope before returning group claims: + +- a Settings-configured provider can request that scope +- Pulse can receive the configured group claim +- allowed-group checks use that claim +- group role mappings use that claim +- a matching group grants the mapped Pulse role +- a non-matching or missing group still fails closed + +The v6 SSO identity invariant remains: + +- the provider-scoped subject is the stable SSO principal +- `preferred_username`, email, or display name must not become the canonical session owner +- local-user linking by `preferred_username` must not be reintroduced as the authorization model +- app chrome should continue to display the IdP user-facing claim instead of the internal provider-scoped principal + +## Non-Goals + +This fix does not need to redesign SSO. + +This fix does not need to change: + +- SAML behavior +- proxy auth +- local username/password auth +- paid-tier gating +- the provider-scoped SSO principal model +- the visible v6 Settings navigation model + +The callback/auth-routing failure reported in #1533 is related OIDC fallout, but it is distinct from the missing group-scope path unless evidence proves a shared root cause. + +## Not Fixed If + +The issue is not fixed if any of the following remain true: + +- the API accepts custom OIDC scopes, but the Settings UI cannot configure them +- the Settings UI has a groups claim field, but the authorization request still omits the configured group scope +- Pulse globally adds `groups` to every OIDC provider without preserving admin-configured scope intent +- role mapping works only when the IdP happens to return groups under the default `openid profile email` request +- role mapping depends on matching a local Pulse user by `preferred_username` +- the display label regresses to `sso:oidc:...` +- existing providers with no custom scopes break +- reporter retest is requested before a release artifact actually contains the fix + +## Required Proof + +A complete fix needs proof for these outcomes: + +- A Settings-created OIDC provider can save a non-default scope set such as `openid profile email groups`. +- Reopening that provider in Settings shows the same scope set. +- The authorization request generated for that provider includes the saved scope set. +- An OIDC callback containing the configured groups claim grants the mapped role. +- An OIDC callback without a matching group still fails closed. +- Existing providers with empty or missing scopes still use `openid profile email`. +- The #1535 display-label fix remains intact. +- The #1533 SSO button/discovery fix remains intact. +- Tests cover both backend payload persistence and the Settings UI path that a normal administrator uses. +- Browser proof exercises the Settings -> Security -> Single Sign-On create/edit path, not only source-level payload builders.