Skip to content

fix(oidc): pass OIDC_SCOPES as a list of scopes instead of a single string - #2813

Open
chrisblech wants to merge 1 commit into
simple-login:masterfrom
chrisblech:fix_oidc_scopes
Open

chrisblech wants to merge 1 commit into
simple-login:masterfrom
chrisblech:fix_oidc_scopes

Conversation

@chrisblech

Copy link
Copy Markdown
Contributor

Fixes #2812

OIDC_SCOPES was passed to OAuth2Session wrapped in a single-element list
(scope=[OIDC_SCOPES]). oauthlib's scope_to_list does not split list elements,
so the requested scope set ended up as {"openid email profile"} — one element
containing spaces — while the provider's token response parses to
{"openid", "email", "profile"}. The two sets can never be equal, so
validate_token_parameters raises and the callback returns a 500.

This affects every provider that includes scope in the token response
(Authentik does so unconditionally; RFC 6749 §3.3 makes it optional when the
granted scope matches the request, which is why it goes unnoticed elsewhere).
The value documented in example.env is affected as well.

Changes

  • app/auth/views/oidc.py: pass OIDC_SCOPES directly at both call sites.
  • app/config.py: parse OIDC_SCOPES into a list and add a default of
    openid email profile. Previously an unset variable produced scope=[None],
    which oauthlib serialises to the literal string "None". The default matches
    example.env and covers the OIDC_NAME_FIELD default of name, which needs
    the profile scope.

OIDC_SCOPES has exactly three references in the codebase, all touched here, and
no test refers to it, so the type change from str to list[str] is contained.

Testing

Verified against Authentik with OIDC_SCOPES unset and with
OIDC_SCOPES=openid email profile; both complete the login. Confirmed with
OAUTHLIB_RELAX_TOKEN_SCOPE removed from the environment, so the scope check is
actually exercised. tests/auth/test_oidc.py passes.

Not included

Two adjacent problems in the same file are deliberately left out to keep this
reviewable, and can follow as separate issues:

  • create_user() passes name=oidc_user_data.get(OIDC_NAME_FIELD), which raises
    TypeError on name[:100] when the claim is absent.
  • The callback does not check email_verified before matching the email claim
    against an existing account.

@acasajus
acasajus enabled auto-merge (rebase) September 17, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OIDC login fails with "Scope has changed" when OIDC_SCOPES contains more than one scope

2 participants