Skip to content

fix(session): send the new token after regenerate_session_id - #137

Merged
allen0099 merged 1 commit into
masterfrom
fix/regenerate-sends-new-token
Sep 25, 2026
Merged

allen0099 merged 1 commit into
masterfrom
fix/regenerate-sends-new-token

Conversation

@allen0099

Copy link
Copy Markdown
Owner

Closes #103.

Problem

docs/SESSION.md recommends regenerate_session_id() after login to prevent session fixation. That call gives the loaded Session a new ID and deletes the old record. Neither middleware noticed:

  • FastAPICacheXSessionMiddleware: _write_session() returned the loaded, old token.
    • Over the cookie transport, the cookie then named the deleted record, and the next request was logged out.
    • Over the header transport, the client got no token at all.
  • Deprecated SessionMiddleware: when sliding expiration had renewed the loaded token, the handler's new token in the response header was overwritten with the renewed token for the old ID.

Change

  • SessionManager.issue_token(session) -> str: a new public helper that signs a token for the session's current ID and expiry. create_session, sliding renewal and regenerate_session_id now all use it instead of repeating _create_token + to_string.
  • FastAPICacheXSessionMiddleware:
    • It records the session ID it loaded.
    • At http.response.start, _response_tokens() compares it with the backend session's current ID. If the ID changed, the current and fresh tokens both become issue_token(session).
    • The existing branches then deliver the new token over the request's transport: modified data → _write_session, untouched data → the "fresh token" branch that sliding renewal already used.
  • SessionMiddleware: the same comparison. A regenerated session gets issue_token(session) in the response header, ahead of any renewed token.
  • regenerate_session_id docstring: it now says the session is changed in place and that the middleware delivers the token.
  • docs/SESSION.md "Regenerate the Session ID After Login":
    • The example now shows the middleware flow (SessionDep + SessionManagerDep).
    • The manual form passes ip_address/user_agent; without them get_session raises when bindings are on.
    • The "old token no longer resolves" claim is now true.

Tests

In tests/session/test_starlette_middleware.py, each case is parametrized over whether the handler also writes to request.session, and whether the load triggered sliding renewal:

  • Cookie transport: Set-Cookie carries a token that resolves to the session with its data, and the old token raises SessionNotFoundError.
  • Header transport: the response header carries the new token, and no cookie is set.
  • Deprecated SessionMiddleware: the header carries the new ID's token, with and without renewal.

All 10 cases fail against the old code.

Checks

  • ruff check / format on fastapi_cachex, tests and scripts
  • mypy strict on the package; mypy on tests and scripts
  • pytest against live Redis and Memcached (CACHEX_REQUIRE_LIVE_SERVERS=1): 754 passed, 100% coverage
  • zensical build --strict

FastAPICacheXSessionMiddleware re-sent the loaded token after a handler
regenerated the request's session ID, so the cookie named a deleted
record and the user was logged out; header clients got no token. The
deprecated SessionMiddleware could overwrite the new token with a renewed
one for the old ID. Both now compare the session ID with the loaded one
and issue a token for the new ID. Token signing moves into
SessionManager.issue_token().

Closes #103
@allen0099
allen0099 merged commit 6cfa0a5 into master Sep 25, 2026
10 checks passed
@allen0099
allen0099 deleted the fix/regenerate-sends-new-token branch September 26, 2026 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

regenerate_session_id() re-sends the old token under FastAPICacheXSessionMiddleware

1 participant