feat(oidc): permit auto/manual oidc account link/unlink

link are checking that email matches, +email alias are normalize into email
if email is already used on another account, link is not possible
not usurpation risk as the IDP is choosen by the admin
This commit is contained in:
Edouard Vanbelle
2026-08-08 17:19:05 +02:00
parent d8b3f2e026
commit e9495a63ad
20 changed files with 1791 additions and 135 deletions
+1
View File
@@ -222,6 +222,7 @@ See the [OIDC configuration guide](/config/oidc) for details.
| `OXICLOUD_OIDC_SCOPES` | `openid profile email` | Requested scopes |
| `OXICLOUD_OIDC_FRONTEND_URL` | `http://localhost:8086` | Frontend URL to redirect to after login |
| `OXICLOUD_OIDC_AUTO_PROVISION` | `true` | Auto-create users on first SSO login (JIT provisioning) |
| `OXICLOUD_OIDC_AUTO_LINK_EMAIL_MATCH` | `true` | When subject-lookup misses on an OIDC login BUT the IdP-returned email (with `email_verified=true`) matches an existing local user (after `+alias` normalization), auto-link the OIDC identity to that user instead of refusing. Refuses on ambiguity (>1 local user normalises to same email) or if the matched user is already linked to a different identity. Safe under single-IdP trust model (admin chose the IdP); unsafe for future multi-IdP federation. Set `false` for postures requiring explicit consent for every link. See [OIDC account linking plan](../plan/oidc-account-linking.md). |
| `OXICLOUD_OIDC_ADMIN_GROUPS` | — | Comma-separated OIDC groups that grant admin role |
| `OXICLOUD_OIDC_DISABLE_PASSWORD_LOGIN` | `false` | **DEPRECATED** — emits boot warning; slated for removal in next major release. Use `OXICLOUD_AUTH_METHODS=oidc` (and optionally `OXICLOUD_AUTH_POLICIES=auto_redirect_if_standalone_oidc` for server-side `/login` redirect) instead. Still removes `password` from the effective allowlist when set to `true` — kept working so upgrading deployments don't break. |
| `OXICLOUD_OIDC_PROVIDER_NAME` | `SSO` | Display name for the provider shown in UI |
+19 -2
View File
@@ -47,6 +47,7 @@ OXICLOUD_OIDC_PROVIDER_NAME="Authentik"
| `OXICLOUD_OIDC_SCOPES` | `openid profile email` | Requested scopes |
| `OXICLOUD_OIDC_FRONTEND_URL` | `http://localhost:8086` | Where to redirect the browser after auth |
| `OXICLOUD_OIDC_AUTO_PROVISION` | `true` | Auto-create users on first login |
| `OXICLOUD_OIDC_AUTO_LINK_EMAIL_MATCH` | `true` | Auto-link existing local users to their OIDC identity when the IdP-returned email (with `email_verified=true`) matches an existing local account. See narrative below. |
| `OXICLOUD_OIDC_ADMIN_GROUPS` | — | OIDC groups that grant admin role |
| `OXICLOUD_OIDC_DISABLE_PASSWORD_LOGIN` | `false` | **DEPRECATED** — use `OXICLOUD_AUTH_METHODS=oidc` instead. Emits a boot warning; slated for removal in next major release. |
| `OXICLOUD_OIDC_PROVIDER_NAME` | `SSO` | Label shown on the login button |
@@ -68,11 +69,27 @@ If `OXICLOUD_OIDC_ENABLED=true` but `issuer_url`, `client_id`, or `client_secret
OIDC users are matched by the pair:
- `oidc_provider`
- `oidc_subject`
- `federation_issuer` (the id_token `iss` claim, canonical issuer URL)
- `federation_subject` (the id_token `sub` claim)
This allows one external identity to map to one local user record and supports just-in-time provisioning when `OXICLOUD_OIDC_AUTO_PROVISION=true`.
### Auto-linking existing local users
When `OXICLOUD_OIDC_AUTO_LINK_EMAIL_MATCH=true` (default) and the subject-based lookup misses on an OIDC login, OxiCloud tries to match by the IdP-returned email address instead. If exactly one local user's email matches (under `+alias`-stripping normalization) AND the IdP returned `email_verified=true`, the OIDC identity is auto-linked to that existing user — no admin round-trip, no manual SQL, no self-service flow needed. Great UX for enabling SSO on top of an existing user base.
Refusal cases (fall through to the standard "email already exists" error):
- `email_verified=false` on the IdP claims — audit event `federation.auto_link_refused` with `reason=auto_link_email_not_verified`.
- More than one local user's email normalizes to the same value (rare but possible with `alice@example.com` and `alice+work@example.com`) — refused as `email_ambiguous`.
- The matched user is already linked to a different OIDC identity — refused as `already_linked_elsewhere`.
Security model: safe under OxiCloud's single-IdP configuration (admin explicitly chose and configured the IdP; the `email_verified` gate means the IdP has vouched for the user's ownership of that email). NOT safe for future multi-IdP federation where any WebFinger-discovered IdP is accepted — deferred to that flow.
For users who need explicit consent for every OIDC link (compliance requirements), set `OXICLOUD_OIDC_AUTO_LINK_EMAIL_MATCH=false`. The self-service link flow (profile page "Connect Single Sign-On" button) remains available regardless.
Full decision tree and safety-check details in [`docs/plan/oidc-account-linking.md`](../plan/oidc-account-linking.md).
## Provider Examples
### Keycloak