Skip to content

fix(session): make request.session.clear() log out regardless of data - #258

Merged
allen0099 merged 1 commit into
masterfrom
fix/session-clear-logout-227
Sep 26, 2026
Merged

allen0099 merged 1 commit into
masterfrom
fix/session-clear-logout-227

Conversation

@allen0099

Copy link
Copy Markdown
Owner

Closes #227.

Problem

FastAPICacheXSessionMiddleware decided what an emptied request.session meant from whether the loaded data started out empty. That caused two bugs:

  1. clear() did not log out a session with empty data (the issue). A user session created with create_session(user) and no data has data == {}, so request.session.clear() changed nothing observable. The session stayed alive and the user stayed logged in.
  2. Removing the last key logged the user out (found while reproducing request.session.clear() does not log out a session whose data is empty #227). request.session.pop("flash") on a user session whose only key was a flash message took the delete branch and destroyed the whole session.

Fix

request.session is now a small Session subclass that records an explicit clear().

  • clear() on a loaded session is a logout.
    • The backend session is deleted whatever its data held.
    • A cookie client gets an expiring Set-Cookie.
    • Keys written after clear() in the same request go into a new anonymous session under a new ID, never the logged-out one.
  • Emptying the dict with del/pop() is not a logout.
    • A session with a user is saved with empty data.
    • An anonymous session holds nothing and is still deleted (Starlette parity).
  • With no session loaded, behaviour is unchanged. For example, set-then-delete on a fresh request still sends no cookie.

The response handling moved into a _persist() helper so that __call__ stays under ruff's statement limit.

Tests

New tests in tests/session/test_starlette_middleware.py:

  • test_clear_logs_out_a_session_with_empty_data[cookie|header]
  • test_popping_the_last_key_keeps_a_user_logged_in
  • test_popping_the_last_key_deletes_an_anonymous_session (parity guard, passes before and after)
  • test_writing_after_clear_starts_a_new_anonymous_session[cookie|header]

Mutation check: with the old middleware, exactly the five new behaviour tests fail and every existing test passes. The full suite passes against live Redis and Memcached (932 passed, 99.96% coverage). Both docs builds pass with --strict.

Docs

  • SESSION.md (EN and zh-TW), under the request.session bullets: clear() is a logout, and del/pop() is not.
  • CHANGELOG: ### Security entry.
  • CLAUDE.md: one line.

clear() on a loaded session now always deletes it, even when its data was
already empty, and keys written after clear() start a new anonymous session.
Removing the last key with del/pop() no longer logs a user out: a session with
a user is saved with empty data, an anonymous one is still deleted.

Closes #227
@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 a0f1ab4 into master Sep 26, 2026
11 checks passed
@allen0099
allen0099 deleted the fix/session-clear-logout-227 branch September 26, 2026 21:39
@allen0099 allen0099 added the security Security vulnerability or hardening label Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working security Security vulnerability or hardening session Session management subsystem

Projects

None yet

Development

Successfully merging this pull request may close these issues.

request.session.clear() does not log out a session whose data is empty

1 participant