Skip to content

fix(oidc): simplify refresh identity validation - #1905

Open
nijel wants to merge 1 commit into
python-social-auth:masterfrom
nijel:oidc-validate
Open

fix(oidc): simplify refresh identity validation#1905
nijel wants to merge 1 commit into
python-social-auth:masterfrom
nijel:oidc-validate

Conversation

@nijel

@nijel nijel commented Aug 7, 2026

Copy link
Copy Markdown
Member

Require core claims before login and validate refreshed ID tokens without relying on the authentication-request nonce.

Persist an immutable identity context while retaining the latest ID token. This removes UID and backend-specific migration logic, accepts valid authorized-party claim changes and audience reordering, and avoids false mismatches for custom user IDs and repeated refreshes.

Legacy associations establish their baseline on the first fully validated refresh; malformed stored contexts require reauthentication.

Require core claims before login and validate refreshed ID tokens
without relying on the authentication-request nonce.

Persist an immutable identity context while retaining the latest ID token.
This removes UID and backend-specific migration logic, accepts valid
authorized-party claim changes and audience reordering, and avoids false
mismatches for custom user IDs and repeated refreshes.

Legacy associations establish their baseline on the first fully validated
refresh; malformed stored contexts require reauthentication.
@nijel nijel self-assigned this Aug 7, 2026
@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.73684% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.36%. Comparing base (8a79067) to head (f77f0ac).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
social_core/backends/open_id_connect.py 82.50% 3 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1905      +/-   ##
==========================================
+ Coverage   87.31%   87.36%   +0.04%     
==========================================
  Files         352      352              
  Lines       13700    13713      +13     
  Branches      674      674              
==========================================
+ Hits        11962    11980      +18     
+ Misses       1494     1490       -4     
+ Partials      244      243       -1     
Flag Coverage Δ
unittests 87.36% <94.73%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f77f0acb92

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +521 to +525
if previous_context is None:
# Legacy associations have no original claim context. Their refreshes
# were historically not continuity-checked, so establish the baseline
# from this fully validated refresh token and enforce it thereafter.
previous_context = self._id_token_context(self.id_token)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify legacy associations before adopting the refreshed subject

For any association created before 5.1, _oidc_id_token_context is absent, so if its first refresh returns a correctly signed token for a different sub, this branch accepts that subject and makes it the permanent baseline while retaining the original local association and UID. That defeats the refresh-continuity protection precisely during the upgrade window and can attach another provider identity's tokens to the existing user; retain the UID comparison for standard sub-based backends and require reauthentication when the stored UID cannot establish continuity.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The protection was non-existing before. This starts to protect new refreshes. To keep the code limited, the legacy context is not migrated.

)
from social_core.utils import cache

_ID_TOKEN_CONTEXT_KEY = "_oidc_id_token_context"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the context-key attribute on the backend class

Replacing OpenIdConnectAuth.ID_TOKEN_CONTEXT_KEY, which shipped in 5.1.0 and was accessed through backend instances, with a private module constant breaks integrations that reference the attribute and silently ignores subclasses that override it to control the storage key. Preserve the class attribute and have the implementation use it so upgrades do not raise AttributeError or leave previously customized contexts unread.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having it on class gave false impression that this is something to override, what it is not.

Comment on lines +444 to 445
if "azp" in claims and claims["azp"] != client_id:
raise AuthTokenError(self, "Incorrect id_token: azp")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require an authorized party for multi-audience tokens

When aud contains multiple client IDs and azp is absent, this now accepts the token merely because PyJWT finds this backend's client ID among the audiences. The authorized party is therefore ambiguous, contrary to the OIDC multi-audience validation check that the previous implementation enforced, so a token issued under another client's authorization can be accepted here; retain the missing-azp rejection whenever aud has more than one entry.

Useful? React with 👍 / 👎.

Comment on lines +375 to +376
self.validate_required_id_token_claims(claims)
self.validate_temporal_claims(claims)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Continue invoking the refresh-claim validation hook

The 5.1.0 base class exposed validate_refresh_claims() specifically for validating claims independently of the authentication request, but this path now bypasses that method and calls validate_temporal_claims() directly. Any third-party OIDC backend overriding the hook to enforce provider-specific refresh claims will silently stop running those checks after upgrading; keep invoking the hook and move the new common required-claim checks around its default implementation.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that the intermediate validate_refresh_claims is not that useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant