Resolve the caller from the explicit credential, not the session cookie - #72225
Open
potiuk wants to merge 2 commits into
Open
Resolve the caller from the explicit credential, not the session cookie#72225potiuk wants to merge 2 commits into
potiuk wants to merge 2 commits into
Conversation
`JWTRefreshMiddleware` resolves a user from the `_token` cookie alone and stamps it on `request.state` together with the trust sentinel. `get_user()` returned that cached user before it looked at `bearer_credentials` or `oauth_token`, so on every core-API route the effective precedence was cookie over bearer -- the inverse of the order the function itself codes. A request carrying both a session cookie and an explicit `Authorization: Bearer` token therefore executed, and was audit-logged, as the cookie's principal rather than the identity the client asked to act as. The cached user is now honoured only when the request carries no explicit credential, which is the case it exists for: a browser session whose token the middleware has just refreshed. When a bearer or OAuth2 token is present it is resolved instead.
potiuk
requested review from
bugraoz93,
choo121600,
ephraimbuddy,
henry3260,
jason810496,
pierrejeambrun,
rawwar and
shubhamraj-git
as code owners
August 28, 2026 17:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
get_user()codes an explicit precedence — bearer, then OAuth2, then thesession cookie:
That block was never reached when a session cookie was present.
JWTRefreshMiddlewareruns first, resolves a user from the
_tokencookie alone, and stamps it onrequest.statetogether with the trust sentinel;get_user()returned that cacheduser up front, before looking at either explicit credential. The effective order on
every core-API route was therefore cookie over bearer — the inverse of what the
function reads as doing.
So a request carrying both a cookie and an explicit
Authorization: Bearertoken ranas the cookie's principal. The token the client deliberately presented was ignored,
and the request was recorded in the audit log under the wrong identity.
The change
The cached user is honoured only when the request carries no explicit credential —
which is the case it exists for: a browser session whose token the middleware has just
refreshed. When a bearer or OAuth2 token is present, that token is resolved instead.
The trust-sentinel check is unchanged and still guards the cached-user path; it has
simply moved inside the no-explicit-credential branch.
Behaviour
Only the two mixed-credential rows change. A client that sends one credential is
unaffected, and cookie-only browser sessions keep the refresh behaviour intact.
Tests
test_get_user_explicit_credential_beats_cookie_user, parametrised over bearer andOAuth2: a trusted cookie-derived user is stamped on
request.stateand an explicitcredential is supplied; the explicit one must win. Both fail if the source change is
reverted.
The existing
test_get_user_with_trusted_request_statestill passes unmodified — itsupplies no explicit credential, so it exercises the path that was deliberately kept.
140 passed across
core_api/test_security.pyandauth/middlewares/; ruff clean.🤖 Generated with Claude Code