Skip to content

fix(session): vary on the headers read to find the session token - #209

Merged
allen0099 merged 1 commit into
masterfrom
fix/session-vary-header
Sep 26, 2026
Merged

allen0099 merged 1 commit into
masterfrom
fix/session-vary-header

Conversation

@allen0099

Copy link
Copy Markdown
Owner

Closes #168

Problem

FastAPICacheXSessionMiddleware added Vary: Cookie whenever a handler touched request.session, whatever transport carried the token. A request authenticated with X-Session-Token or Authorization: Bearer never reads the cookie. Its response depends on those headers, so a shared cache that honours Vary would key it on Cookie alone. Repro: an app that reads request.session, requested with only X-Session-Token: <token>, returns Vary: Cookie, and nothing for X-Session-Token.

Fix

  • New _read_header_token(connection, config) returns the token and the header names it read, in token_source_priority order, stopping at the one that carried the token. _extract_header_token keeps its signature and delegates to it.
  • The middleware adds Vary for each of those headers. It adds Cookie only when no header carried a token, because only then is the cookie read.

Resulting Vary with the default config (token_source_priority=["header", "bearer"]):

Token arrived in Vary
X-Session-Token X-Session-Token
Authorization: Bearer X-Session-Token, Authorization
cookie, or no token X-Session-Token, Authorization, Cookie

The headers checked before the cookie are listed on cookie responses too: had the client sent one, it would have won over the cookie, so the response depends on its absence. When use_bearer_token=False, Authorization is left out. As before, nothing is added unless the handler accessed request.session.

The deprecated SessionMiddleware has never added Vary; this PR leaves it unchanged.

Tests

New tests in tests/session/test_starlette_middleware.py:

  • header token, bearer token, cookie token, and bearer disabled: each asserts the exact Vary set;
  • no Vary when the session is not accessed (guards existing behaviour).

With middleware.py restored from master, exactly the four transport tests fail; they all get {'cookie'}.

ruff, mypy --strict, and the full suite against live Redis and Memcached (CACHEX_REQUIRE_LIVE_SERVERS=1) pass: 866 passed, 100% coverage.

docs/SESSION.md and its zh-TW version describe the new rule.

CHANGELOG entry

Section: Fixed (to be added via #206)

- **The session middleware varies on the header that carried the token.**
  `FastAPICacheXSessionMiddleware` added `Vary: Cookie` whenever
  `request.session` was accessed, even when the token came in
  `X-Session-Token` or `Authorization`, so a shared cache could key those
  responses on the wrong header. It now varies on every request header it
  read to find the token, and on `Cookie` only when no header carried one.
  ([#168](https://github.com/allen0099/FastAPI-CacheX/issues/168))

FastAPICacheXSessionMiddleware added Vary: Cookie whenever a handler
touched request.session, even when the token came in X-Session-Token or
Authorization. Those requests never read the cookie, and a shared cache
keyed on Cookie could hand one header client's response to another.

Vary on every request header read to find the token, in
token_source_priority order, and add Cookie only when no header carried
one, since only then is the cookie read.

Closes #168
@allen0099 allen0099 added this to the 0.3.8 milestone Sep 26, 2026
@allen0099 allen0099 added bug Something isn't working session Session management subsystem labels Sep 26, 2026
@allen0099
allen0099 merged commit 717d4f4 into master Sep 26, 2026
11 checks passed
@allen0099
allen0099 deleted the fix/session-vary-header branch September 26, 2026 14:35
allen0099 added a commit that referenced this pull request Sep 27, 2026
Copies the CHANGELOG sections of #207, #208, #209, #211, #212, #272,
#273, #274, #275, #276, #279 and #284 into Unreleased. The #273 entry
drops expire_if_equals from its list of methods that changed, since that
primitive is new in 0.3.8.
allen0099 added a commit that referenced this pull request Sep 27, 2026
Copies the CHANGELOG sections of #207, #208, #209, #211, #212, #272,
#273, #274, #275, #276, #279 and #284 into Unreleased. The #273 entry
drops expire_if_equals from its list of methods that changed, since that
primitive is new in 0.3.8.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working session Session management subsystem

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Vary: Cookie only when the session travels in a cookie

1 participant