Session.clear() set the id to None before clearing, so on_update
assigned a new id and save_session deleted the store entry of that new
id. The entry of the old id with the user_id stayed valid: a copied
cookie survived the logout, and a session "logged out" by the auth
middleware for a disabled user was valid again after re-enabling.
- Session.clear() now removes the store entry of the current id.
- Deactivating a user removes all of their sessions immediately, like
deleting a user does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Anonymous users must not get cookies or session store entries, so a
CSRF token for /login is not possible. Instead, every unsafe request is
checked statelessly via Sec-Fetch-Site, falling back to Origin. Requests
without both headers are not from a browser and are accepted. The
expected origin comes from the new MAX_ORIGIN setting, which is required
outside of debug mode.
Further CSRF changes:
- Do not delete the CSRF token on a failed check. It added no security,
but invalidated forms in open tabs and could be triggered cross-site.
- Treat HEAD and OPTIONS as safe methods.
Keep the login page free of sessions:
- Show login messages via ?msg=login_required|logout (fixed keys only)
and the login error via the template context instead of flash().
- Only send the session deletion cookie if the client sent a cookie.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
isort's lines_after_imports = 2 conflicted with black, so run-cleanup
reverted black's changes. Use isort's black profile instead and apply the
resulting formatting. Also fix the flake8 errors in check_email_mixin and
drop a leftover debug print.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- scripts/prod: entrypoint, db-setup and schema.sql shipped in the image
- scripts/dev: seed/clear db, psql and cleanup, run natively in the
devcontainer instead of via docker-compose
- move setup.cfg to the repo root so linters pick it up
- Makefile targets run directly in the devcontainer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move logging setup into max/log.py. The file handler is only used outside
debug mode and its directory is configurable via LOG_DIR. FLASK_ENV is
deprecated in Flask; debug mode is now controlled via FLASK_DEBUG.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>