Skip to content

feat(session)!: add logout() and login(keep=), make Session.user read-only - #416

Merged
allen0099 merged 1 commit into
masterfrom
feat/256-logout-readonly-user
Sep 30, 2026
Merged

allen0099 merged 1 commit into
masterfrom
feat/256-logout-readonly-user

Conversation

@allen0099

Copy link
Copy Markdown
Owner

Closes #256. This is the rest of the proposal after #415 (cookie defaults): logout(), login(keep=) and a read-only Session.user.

Changes

  • await logout(request) (fastapi_cachex.session):
    • deletes the session from the backend at once, so its token stops resolving before the response is sent;
    • then goes through the request.session.clear() path, so the middleware expires a cookie client's cookie;
    • for the rest of the request, get_session finds no session, keys written afterwards start a new anonymous session, and a later login() starts a new session;
    • returns True, or False when no session was loaded or started in the request;
    • raises RuntimeError without FastAPICacheXSessionMiddleware.
  • login(request, user, *, keep=...):
    • carries only the listed keys over, both from the stored record (before rotation, so the record under the new ID never holds a dropped key) and from what was written to request.session before the call;
    • keep=[] carries nothing; keys written after the call are kept;
    • a different user's session is still dropped entirely, whatever keep lists;
    • a str raises TypeError (keep="cart" would otherwise mean its letters).
  • Session.user is read-only: assigning it raises AttributeError, with a message that points to login() / create_session(user=...).
    • Construction, model_validate and model_copy still accept a user; login() uses a private _attach_user().
    • The __setattr__ override is hidden from type checkers, so typos such as session.dta = ... are still reported.
  • examples/session_login.py logs out with logout().
  • Docs, in both languages:
    • SESSION.md: read-only user, logout(), keep=, and how logout() differs from clear().
    • MIGRATING_0_4.md#login-logout: rewritten for the implemented behaviour.
  • Changelog: 256.added.md (logout(), keep=) and 256.changed.2.md (read-only Session.user).

No 0.3.9 warning for the read-only user: 0.3.x cannot tell which code assigns it, as the migration guide already says.

Tests

  • New tests/session/test_logout_and_keep.py (19 tests):
    • logout() over the cookie, header and bearer transports; without a session; writes after it; login() after it; login() then logout(); outside the middleware;
    • keep= on an anonymous session, a re-login, a new visitor and another user's session, and a string argument;
    • assigning user raises, other fields stay assignable, a user can still be given when a Session is built.
  • test_session_login checks the example's /logout return values.
  • Mutation checks, each caught:
    • removing the __setattr__ guard;
    • skipping the request.session filter or the stored-record filter;
    • skipping the immediate delete in logout();
    • dropping the string check;
    • not emptying request.session for another user's session.
  • Full suite, coverage (99.91%), mypy --strict, mypy tests / scripts, both strict docs builds and tox -e lowest pass.

…-only

`await logout(request)` deletes the session from the backend at once, so
its token stops resolving before the response is sent, and the middleware
expires a cookie client's cookie. It returns False when no session was
loaded or started in the request.

`login(request, user, keep=[...])` carries only the listed keys of the
session the request arrived with over to the logged-in one, both from the
stored record and from what the handler wrote before the call. A string is
rejected with TypeError.

Assigning `Session.user` raises AttributeError, so a session gets a user
only when it is built, from `login()` (which rotates the ID) or from
`SessionManager.create_session(user=...)`.

BREAKING CHANGE: assigning `Session.user` raises AttributeError. Under
FastAPICacheXSessionMiddleware use `login(request, user)`; without it,
create the session with `SessionManager.create_session(user=...)`.

Closes #256
@allen0099 allen0099 added this to the 0.4.0 milestone Sep 30, 2026
@allen0099 allen0099 added enhancement New feature or request session Session management subsystem breaking-change Changes public behaviour or API; needs a minor/major release labels Sep 30, 2026
@allen0099
allen0099 merged commit ec3dfc0 into master Sep 30, 2026
15 checks passed
@allen0099
allen0099 deleted the feat/256-logout-readonly-user branch September 30, 2026 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Changes public behaviour or API; needs a minor/major release enhancement New feature or request session Session management subsystem

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0.4.0: explicit login()/logout() that always rotate the session ID, and __Host- cookie by default

1 participant