P4-05: OIDC login (generic, JIT-provisioned) #16
@@ -19,7 +19,7 @@ The Authorization Code flow (with PKCE) is implemented against the discovered we
|
||||
| Decision | Pick |
|
||||
|---|---|
|
||||
| User lifecycle | **B** — JIT-provision local rows on first OIDC login (`auth_source='oidc'`, `oidc_subject`) |
|
||||
| Role mapping config | **A** — YAML/env, claim name `roles`, default = deny on no-match |
|
||||
| Role mapping config | **A** — YAML/env, claim name configurable (default `groups`, matching Authelia / Keycloak / Authentik), default = deny on no-match |
|
||||
| Username source | `preferred_username`, fallback to `email` |
|
||||
| Username collision with existing local user | **Refuse** with clear remediation message |
|
||||
| Provider config | **Single provider** — `providers:` array can come later |
|
||||
@@ -51,8 +51,9 @@ oidc:
|
||||
issuer: https://auth.example.com # well-known config discovered from this
|
||||
client_id: restic-manager
|
||||
client_secret: ${RM_OIDC_CLIENT_SECRET} # or via _FILE
|
||||
scopes: [openid, profile, email, roles] # 'roles' usually means a custom scope
|
||||
role_claim: roles # default if absent
|
||||
display_name: Authelia # button label "Sign in with <display_name>"; default "SSO"
|
||||
scopes: [openid, profile, email, groups]
|
||||
role_claim: groups # default if absent (matches Authelia / Keycloak / Authentik)
|
||||
role_mapping:
|
||||
rm-admins: admin
|
||||
rm-operators: operator
|
||||
@@ -196,6 +197,8 @@ The IdP is the hard part to test cleanly. Two layers:
|
||||
- **End-session 404 / not advertised** — degrade gracefully; just drop the session and 303 to `/login`. Don't 500 the logout because the IdP doesn't implement RP-initiated logout.
|
||||
- **Username changes at the IdP** — silently keep the local username (matches our locked decision: subject is the stable key, username is display-only). Document.
|
||||
- **Role claim is sometimes a string, sometimes an array, sometimes a comma-separated string** depending on IdP — normalise into `[]string` before mapping. Authelia/Keycloak emit arrays; some custom setups emit strings; handle both.
|
||||
- **Authelia `sub` is an opaque UUID, not the username** (Authelia 4.39+ default for new clients). Don't assume `sub` is human-readable; it's stable but display value is `preferred_username` or `email`. The locked design already keys lookups on `sub` and uses `preferred_username` for the display username, so this is just a correctness note.
|
||||
- **`end_session_endpoint` may not be published** (Authelia doesn't advertise it for many configs). The locked logout flow already degrades to "drop session + redirect to /login" when the discovery doc lacks it; no extra config needed.
|
||||
- **Password-form bypass for OIDC users via /api/auth/login (JSON)** — same rejection rule applies, not just the HTML form.
|
||||
|
||||
## Acceptance
|
||||
|
||||
Reference in New Issue
Block a user