allow importing existing users when the localpart matches in upstream OAuth 2.0 logins

This commit is contained in:
mcalinghee
2025-06-12 16:37:57 +02:00
committed by Olivier D
parent 774c8786ff
commit 1886e73e40
15 changed files with 657 additions and 26 deletions

View File

@@ -2361,6 +2361,14 @@
"template": {
"description": "The Jinja2 template to use for the localpart attribute\n\nIf not provided, the default template is `{{ user.preferred_username }}`",
"type": "string"
},
"on_conflict": {
"description": "How to handle conflicts on the claim, default value is `Fail`",
"allOf": [
{
"$ref": "#/definitions/OnConflict"
}
]
}
}
},
@@ -2397,6 +2405,25 @@
}
]
},
"OnConflict": {
"description": "How to handle an existing localpart claim",
"oneOf": [
{
"description": "Fails the sso login on conflict",
"type": "string",
"enum": [
"fail"
]
},
{
"description": "Adds the oauth identity link, regardless of whether there is an existing link or not",
"type": "string",
"enum": [
"add"
]
}
]
},
"DisplaynameImportPreference": {
"description": "What should be done for the displayname attribute",
"type": "object",

View File

@@ -776,6 +776,12 @@ upstream_oauth2:
localpart:
#action: force
#template: "{{ user.preferred_username }}"
# How to handle when localpart already exists.
# Possible values are (default: fail):
# - `add` : Adds the upstream account link to the existing user, regardless of whether there is an existing link or not.
# - `fail` : Fails the upstream OAuth 2.0 login.
#on_conflict: fail
# The display name is the user's display name.
displayname:

View File

@@ -66,6 +66,30 @@ The template has the following variables available:
- `user`: an object which contains the claims from both the `id_token` and the `userinfo` endpoint
- `extra_callback_parameters`: an object with the additional parameters the provider sent to the redirect URL
## Allow linking existing user accounts
The authentication service supports linking external provider identities to existing local user accounts.
To enable this behavior, the following option must be explicitly set in the provider configuration:
```yaml
claims_imports:
localpart:
on_conflict: add
```
`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.
>
> To mitigate this risk, ensure that this option is only enabled for identity providers where you can guarantee that the attribute mapping `localpart` will reliably and uniquely correspond to the intended local user account.
## Multiple providers behaviour
Multiple authentication methods can be configured at the same time, in which case the authentication service will let the user choose which one to use.