From e9e138e62cae6cb4c9dac0c5992040c5d02b8985 Mon Sep 17 00:00:00 2001 From: Gabgobie <105999094+Gabgobie@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:31:01 +0100 Subject: [PATCH] Enhancement: configurable SSO groups claim (#11841) --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- docs/configuration.md | 8 +++++++- src/paperless/settings.py | 5 +++++ src/paperless/signals.py | 10 +++++++--- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index cc829342d..872c93e44 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -659,7 +659,7 @@ system. See the corresponding : Sync groups from the third party authentication system (e.g. OIDC) to Paperless-ngx. When enabled, users will be added or removed from groups based on their group membership in the third party authentication system. Groups must already exist in Paperless-ngx and have the same name as in the third party authentication system. Groups are updated upon logging in via the third party authentication system, see the corresponding [django-allauth documentation](https://docs.allauth.org/en/dev/socialaccount/signals.html). -: In order to pass groups from the authentication system you will need to update your [PAPERLESS_SOCIALACCOUNT_PROVIDERS](#PAPERLESS_SOCIALACCOUNT_PROVIDERS) setting by adding a top-level "SCOPES" setting which includes "groups", e.g.: +: In order to pass groups from the authentication system you will need to update your [PAPERLESS_SOCIALACCOUNT_PROVIDERS](#PAPERLESS_SOCIALACCOUNT_PROVIDERS) setting by adding a top-level "SCOPES" setting which includes "groups", or the custom groups claim configured in [`PAPERLESS_SOCIAL_ACCOUNT_SYNC_GROUPS_CLAIM`](#PAPERLESS_SOCIAL_ACCOUNT_SYNC_GROUPS_CLAIM) e.g.: ```json {"openid_connect":{"SCOPE": ["openid","profile","email","groups"]... @@ -667,6 +667,12 @@ system. See the corresponding Defaults to False +#### [`PAPERLESS_SOCIAL_ACCOUNT_SYNC_GROUPS_CLAIM=`](#PAPERLESS_SOCIAL_ACCOUNT_SYNC_GROUPS_CLAIM) {#PAPERLESS_SOCIAL_ACCOUNT_SYNC_GROUPS_CLAIM} + +: Allows you to define a custom groups claim. See [PAPERLESS_SOCIAL_ACCOUNT_SYNC_GROUPS](#PAPERLESS_SOCIAL_ACCOUNT_SYNC_GROUPS) which is required for this setting to take effect. + + Defaults to "groups" + #### [`PAPERLESS_SOCIAL_ACCOUNT_DEFAULT_GROUPS=`](#PAPERLESS_SOCIAL_ACCOUNT_DEFAULT_GROUPS) {#PAPERLESS_SOCIAL_ACCOUNT_DEFAULT_GROUPS} : A list of group names that users who signup via social accounts will be added to upon signup. Groups listed here must already exist. diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 532a2bc36..9ad0fea4d 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -540,6 +540,11 @@ SOCIALACCOUNT_PROVIDERS = json.loads( ) SOCIAL_ACCOUNT_DEFAULT_GROUPS = __get_list("PAPERLESS_SOCIAL_ACCOUNT_DEFAULT_GROUPS") SOCIAL_ACCOUNT_SYNC_GROUPS = __get_boolean("PAPERLESS_SOCIAL_ACCOUNT_SYNC_GROUPS") +SOCIAL_ACCOUNT_SYNC_GROUPS_CLAIM: Final[str] = os.getenv( + "PAPERLESS_SOCIAL_ACCOUNT_SYNC_GROUPS_CLAIM", + "groups", +) + HEADLESS_TOKEN_STRATEGY = "paperless.adapter.DrfTokenStrategy" MFA_TOTP_ISSUER = "Paperless-ngx" diff --git a/src/paperless/signals.py b/src/paperless/signals.py index cfad29dbd..1ed88c051 100644 --- a/src/paperless/signals.py +++ b/src/paperless/signals.py @@ -40,15 +40,19 @@ def handle_social_account_updated(sender, request, sociallogin, **kwargs): extra_data = sociallogin.account.extra_data or {} social_account_groups = extra_data.get( - "groups", + settings.SOCIAL_ACCOUNT_SYNC_GROUPS_CLAIM, [], ) # pre-allauth 65.11.0 structure if not social_account_groups: # allauth 65.11.0+ nests claims under `userinfo`/`id_token` social_account_groups = ( - extra_data.get("userinfo", {}).get("groups") - or extra_data.get("id_token", {}).get("groups") + extra_data.get("userinfo", {}).get( + settings.SOCIAL_ACCOUNT_SYNC_GROUPS_CLAIM, + ) + or extra_data.get("id_token", {}).get( + settings.SOCIAL_ACCOUNT_SYNC_GROUPS_CLAIM, + ) or [] ) if settings.SOCIAL_ACCOUNT_SYNC_GROUPS and social_account_groups is not None: