Skip to content

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

Description

@allen0099

Follow-up to #225 / #255. #255 adds an opt-in rotate_session_id(request) for 0.3.8. A helper the developer has to remember is the weakest form of the fixation defence, and some of the stronger forms are breaking changes, so this collects them for 0.4.0.

Principle

Becoming authenticated happens through one explicit API, and that API always issues a new session ID. There is no other way to attach a user through the library, so the library's own identity (session.user, get_session, UserSessionDep) cannot be fixated.

Limit: an application that decides "logged in" from its own request.session keys (request.session.get("user_id"), the usual Starlette pattern) is outside what the library can see. The docs must say this up front: use the library's identity, or rotate yourself.

Proposal

  1. Explicit login() / logout()
    • await login(request, user) (exact shape to be decided: request-level function, or a method on the request's session) attaches the user and always rotates the ID. The middleware sends the new token through the request's transport.
    • logout() deletes the record. request.session.clear() keeps meaning logout, for compatibility.
    • Session.user becomes read-only outside these calls. Code that sets it directly today breaks: this is the breaking part.
    • rotate_session_id() stays for privilege changes without a new user (or becomes elevate()).
  2. A login starts a new session instead of promoting the anonymous one
    • login() creates a fresh record, copies only the data the caller asks to keep (keep=["cart"], or all by default: to be decided), and deletes the old one.
    • Nothing an attacker put into a planted session is carried into the authenticated one unless the application chose to keep it.
  3. Short grace period after rotation
  4. Cookies that are hard to plant, by default
    • Default cookie_name becomes __Host-session with Secure. Browsers refuse __Host- cookies set from a subdomain or with a Domain attribute, which removes the usual fixation vector.
    • Local development over plain http needs an explicit opt-out, and SessionConfig should reject contradictory settings (a __Host- name with cookie_domain, a path other than /, or no Secure).
    • Changing the default name logs every existing user out once; the changelog must say so.

Optional, can be split out:

Differences from Starlette's SessionMiddleware after this change

The dict API stays compatible: request.session still reads and writes the same way. What changes is the login semantics: writing a key no longer means "logged in" as far as the library is concerned. The migration guide needs a short table:

  • where data lives;
  • login and logout;
  • revocation;
  • cookie defaults;
  • size limit;
  • the need for a backend.

Related 0.4.0 items to ship together

One migration section in the docs covering all of them is easier on users than several separate ones.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsessionSession management subsystem

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions