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
+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