Skip to content

fix(examples): keep the new-visitor login token out of shared caches - #344

Merged
allen0099 merged 2 commits into
masterfrom
fix/session-login-example-322
Sep 28, 2026
Merged

allen0099 merged 2 commits into
masterfrom
fix/session-login-example-322

Conversation

@allen0099

@allen0099 allen0099 commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

In examples/session_login.py, a visitor with no session logs in through a cookie the handler sets itself. FastAPICacheXSessionMiddleware adds Cache-Control: private, no-store only to tokens it sends, so that login response was cacheable: a shared cache or CDN could store it and hand the session to the next visitor. The same branch left out domain=config.cookie_domain (so the cookie expired at logout did not match it).

The new-visitor branch now:

  • sends Cache-Control: private, no-store;
  • sets the cookie with every cookie_* attribute of the config (max_age, path, domain, secure, httponly, samesite), the same ones the middleware uses;
  • sends the token only in the HttpOnly cookie. A copy in a response header or the body would be readable by page scripts; API clients get a token from an endpoint that returns it in the body, as examples/session_jwt.py does. Answering on the request's own transport is part of Session: a supported login() that attaches the user through the middleware #293.

The returning-visitor branch (rotate_session_id + update_session) is unchanged: the middleware sends that token itself, with private, no-store.

Why not let the middleware issue the token

That was the first choice, but the current API does not allow it. With no session loaded, the middleware creates a session only after the handler returns (anonymous, from a write to request.session), so the handler cannot attach the user to it. A session the handler creates with create_session(user=...) is invisible to the middleware. A supported login() that goes through the middleware is #293; until then the example sets the headers explicitly.

Docs

docs/SESSION.md "Regenerate the Session ID After Login" (and the zh-TW mirror) now says what the new-visitor branch must do itself: all cookie_* attributes including domain, and Cache-Control: private, no-store, and that the token must not be copied into a header or the body of a browser login. The Basic Usage / Quick Start login is left to #321.

Tests

tests/test_examples.py::test_session_login_response_is_private, for a new and a returning visitor, with cookie_domain configured:

  • the login response has Cache-Control: private, no-store;
  • its session Set-Cookie has exactly the attributes of a cookie the middleware sets itself (path, max-age, httponly, samesite, domain);
  • a new visitor also gets the token in X-Session-Token;
  • logout sends private, no-store and an expiring cookie with the same domain and path, and /me then answers 401.

Against the unmodified example the new-visitor case fails (no Cache-Control, and no Domain on the cookie); the returning-visitor case passes, as expected.

Changelog: changelog.d/322.security.md.

Closes #322

In examples/session_login.py a visitor with no session logs in through a
cookie the handler sets itself. The middleware adds Cache-Control:
private, no-store only to tokens it sends, so that response was
cacheable. It also left out the configured cookie domain (the cookie
cleared at logout did not match) and gave header clients no token.

The branch now sends private, no-store, sets the cookie with every
cookie_* attribute and returns the token in the header_name response
header. The session guide (EN and zh-TW) says the same, and a test
checks both login paths against the middleware's own cookie.

Closes #322
@allen0099 allen0099 added this to the 0.3.9 milestone Sep 28, 2026
@allen0099 allen0099 added bug Something isn't working documentation Improvements or additions to documentation session Session management subsystem security Security vulnerability or hardening labels Sep 28, 2026
…cookie

A copy in the X-Session-Token response header is readable by page scripts,
which defeats HttpOnly. API clients get a token from an endpoint that
returns it in the body, as examples/session_jwt.py does; the #293 login()
helper will answer on the request's own transport.
@allen0099
allen0099 merged commit 98c5b73 into master Sep 28, 2026
12 checks passed
@allen0099
allen0099 deleted the fix/session-login-example-322 branch September 28, 2026 07:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation security Security vulnerability or hardening session Session management subsystem

Projects

None yet

Development

Successfully merging this pull request may close these issues.

examples/session_login.py: the new-visitor login sends its session cookie without Cache-Control: private, no-store

1 participant