diff --git a/crates/config/src/sections/upstream_oauth2.rs b/crates/config/src/sections/upstream_oauth2.rs index 8c7d6dda2..caee97294 100644 --- a/crates/config/src/sections/upstream_oauth2.rs +++ b/crates/config/src/sections/upstream_oauth2.rs @@ -120,14 +120,14 @@ impl ConfigurationSection for UpstreamOAuth2Config { if matches!( provider.claims_imports.localpart.on_conflict, - OnConflict::Add + OnConflict::Add | OnConflict::Replace | OnConflict::Set ) && !matches!( provider.claims_imports.localpart.action, ImportAction::Force | ImportAction::Require ) { return Err(annotate(figment::Error::custom( - "The field `action` must be either `force` or `require` when `on_conflict` is set to `add`", - )).into()); + "The field `action` must be either `force` or `require` when `on_conflict` is set to `add`, `replace` or `set`", + )).with_path("claims_imports.localpart").into()); } } diff --git a/docs/setup/sso.md b/docs/setup/sso.md index 3442d06bd..62835bb77 100644 --- a/docs/setup/sso.md +++ b/docs/setup/sso.md @@ -69,20 +69,25 @@ The template has the following variables available: ## Allow linking existing user accounts -The authentication service supports linking external provider identities to existing local user accounts. +The authentication service supports linking external provider identities to existing local user accounts if the `localpart` matches. -To enable this behavior, the following option must be explicitly set in the provider configuration: +If the `localpart` given by the upstream provider matches an existing user and the `claims_imports.localpart.action` is set to `force` or `require`, by default the service will refuse to link to that existing account. +This behaviour is controlled by the `claims_imports.localpart.on_conflict` option, which can be set to: + + * `fail` *(default)*: fails the upstream OAuth 2.0 login + * `add`: automatically adds the upstream account to the existing user, regardless of whether the existing user already has another upstream account or not + * `set`: automatically adds the upstream account to the existing user only if there are no other upstream accounts for that provider linked to the user + * `replace`: automatically replaces any upstream account for that provider linked to the user ```yaml -claims_imports: - localpart: - on_conflict: add +upstream_oauth2: + providers: + - id: … + claims_imports: + localpart: + action: force + on_conflict: set ``` -`on_conflict` configuration is specific to `localpart` claim_imports, it can be either: -* `add` : when a user authenticates with the provider for the first time, the system checks whether a local user already exists with a `localpart` matching the attribute mapping `localpart` , _by default `{{ user.preferred_username }}`_. If a match is found, the external identity is linked to the existing local account. -* `fail` *(default)* : fails the sso login. - -To enable this option, the `localpart` mapping must be set to either `force` or `require`. > ⚠️ **Security Notice** > Enabling this option can introduce a risk of account takeover.