login_user now records the user's current password_changed_at on the
session. The next commit will use this marker to invalidate sessions
whose password has been rotated under them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single source of truth for the password policy, to be reused by the
upcoming /profile/password endpoint and (optionally) the create-user
CLI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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).