From 3490be5fb7de5caf8943827c7908473c97378c76 Mon Sep 17 00:00:00 2001 From: mwiegand Date: Sun, 10 May 2026 21:13:31 +0200 Subject: [PATCH] auth: reject inactive users at login + invalidate existing sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two-pronged enforcement so deactivation has effect both for fresh logins and already-issued sessions: - load_current_user(): treat User with active=False as logged-out (sets g.user=None). Existing sessions stop working immediately. - login(): include `not user.active` in the existing 401 condition, so deactivated accounts get the same "invalid credentials" response as wrong-password / unknown-user — no timing oracle for deactivation status. Tests still green (12/12 in test_auth.py). --- l4d2web/auth.py | 5 ++++- l4d2web/routes/auth_routes.py | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/l4d2web/auth.py b/l4d2web/auth.py index 74688f4..718c8d8 100644 --- a/l4d2web/auth.py +++ b/l4d2web/auth.py @@ -27,7 +27,10 @@ def load_current_user() -> None: g.user = None return with session_scope() as db: - g.user = db.scalar(select(User).where(User.id == int(user_id))) + user = db.scalar(select(User).where(User.id == int(user_id))) + # Treat deactivated users as logged-out so existing sessions stop + # working as soon as an admin flips active=False. + g.user = user if user is not None and user.active else None def current_user() -> User | None: diff --git a/l4d2web/routes/auth_routes.py b/l4d2web/routes/auth_routes.py index 1f83a57..6a8559a 100644 --- a/l4d2web/routes/auth_routes.py +++ b/l4d2web/routes/auth_routes.py @@ -48,7 +48,9 @@ def login() -> Response: user = db.scalar(select(User).where(User.username == username)) digest = user.password_digest if user is not None else _TIMING_DUMMY_DIGEST password_ok = verify_password(password, digest) - if user is None or not password_ok: + if user is None or not password_ok or not user.active: + # Same generic response for missing user, wrong password, or + # deactivated account — no timing oracle for deactivation status. return Response("invalid credentials", status=401) login_user(user.id) LOGIN_ATTEMPTS_BY_IP.pop(remote_addr, None)