Skip to content

Keep the token cookie set by a route in JWTRefreshMiddleware - #75217

Merged
vincbeck merged 1 commit into
apache:mainfrom
stegololz:fix/jwt-refresh-middleware-keeps-issued-token
Oct 20, 2026
Merged

vincbeck merged 1 commit into
apache:mainfrom
stegololz:fix/jwt-refresh-middleware-keeps-issued-token

Conversation

@stegololz

Copy link
Copy Markdown
Contributor

JWTRefreshMiddleware appends its own _token Set-Cookie header after the response headers. When the route has already set _token itself, browsers apply the headers in order, so the middleware's cookie overrides the route's cookie:

  • Login undone. An auth manager login route (e.g. a callback under /auth) sets a fresh _token, but the request still carries an expired, revoked or otherwise invalid one. resolve_user_from_token fails, the middleware appends _token=""; Max-Age=0 after the fresh cookie, and the user is logged out right after logging in.
  • Logout undone. /api/v2/auth/logout revokes the presented tokens and deletes the cookie. But if refresh_user returned a user for that same request, the middleware mints a new token before the route runs. It then appends that token after the route's deletion. The new token was never revoked, so the user stays logged in.

The Keycloak provider already works around the login case in its own middleware (request.state.jwt_token_issued). That only helps Keycloak's login route, not other auth managers and not the logout case.

Fix: in send_with_cookies, skip the middleware's _token cookie when the response already sets _token. The cleanup of a stale root-path _token (#64955) is still appended, because it targets a different path.

Tests: three new tests in test_refresh_token.py, which fail without the fix:

  • an invalid token on a request whose route sets _token: only the route's cookie is sent
  • a refreshed user on the logout route: only the deletion is sent, no new token
  • with a sub-path cookie path, the route's token is kept and the stale root cookie is still cleared

Related: #74722, #70783 and #70707 touch the same middleware; this change is independent of them.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Claude Code (Opus 5.5) following the guidelines

JWTRefreshMiddleware appends its own _token Set-Cookie after the
response headers. When the route itself sets _token, the browser
applies the middleware cookie last, so it overrides the route:

- A login route, e.g. an auth manager callback mounted under /auth,
  sets a fresh token while the request still carries an expired or
  invalid one. The middleware appends a deletion and the user is
  logged out right after logging in.
- /api/v2/auth/logout deletes the cookie, but if refresh_user returned
  a refreshed user for that request the middleware appends a newly
  minted, unrevoked token and the user stays logged in.

Skip the middleware token cookie when the response already sets
_token. Stale root-path cookies are still cleared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@stegololz
stegololz requested a review from vincbeck as a code owner October 20, 2026 14:05
@boring-cyborg boring-cyborg Bot added the area:API Airflow's REST/HTTP API label Oct 20, 2026
@vincbeck
vincbeck merged commit 99eff46 into apache:main Oct 20, 2026
67 checks passed
@vatsrahul1001 vatsrahul1001 added the backport-to-v3-4-test Backport to v3-4-test label Oct 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Backport failed to create: v3-4-test. View the failure log Run details

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

Status Branch Result
❌ v3-4-test Commit Link

You can attempt to backport this manually by running:

cherry_picker 99eff46 v3-4-test

This should apply the commit to the v3-4-test branch and leave the commit in conflict state marking
the files that need manual conflict resolution.

After you have resolved the conflicts, you can continue the backport process by running:

cherry_picker --continue

If you don't have cherry-picker installed, see the installation guide.

@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-4-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

Status Branch Result
✅ v3-4-test PR Link

vincbeck pushed a commit that referenced this pull request Oct 23, 2026
…are (#75217) (#75454)

JWTRefreshMiddleware appends its own _token Set-Cookie after the
response headers. When the route itself sets _token, the browser
applies the middleware cookie last, so it overrides the route:

- A login route, e.g. an auth manager callback mounted under /auth,
  sets a fresh token while the request still carries an expired or
  invalid one. The middleware appends a deletion and the user is
  logged out right after logging in.
- /api/v2/auth/logout deletes the cookie, but if refresh_user returned
  a refreshed user for that request the middleware appends a newly
  minted, unrevoked token and the user stays logged in.

Skip the middleware token cookie when the response already sets
_token. Stale root-path cookies are still cleared.
(cherry picked from commit 99eff46)

Co-authored-by: Mathieu Monet <60776491+stegololz@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:API Airflow's REST/HTTP API backport-to-v3-4-test Backport to v3-4-test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants