Invalidate existing sessions on password change/reset (B-34)
Neither self-service password change nor the admin reset invalidated already-issued JWTs — a 24h-lifetime token stayed valid regardless, so a stolen token (or an attacker who already had the old password) kept working past a password change meant to lock them out. The admin reset exists precisely for the "account compromised" case and didn't evict the attacker at all. Add User.token_version (migration 943dbd74d983), embedded in every JWT as a "tv" claim and checked against the DB on every request in get_current_user/get_optional_user; a mismatch reads as session_expired. Both change-password and the admin reset bump it. change-password hands back a freshly minted token so the caller's own session keeps working instead of being logged out by its own request; the admin reset does not, since that session isn't the one making the call. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -205,6 +205,10 @@ async def test_admin_resets_user_password(client):
|
||||
assert refreshed.password_hash != old_hash
|
||||
assert verify_password(new_password, refreshed.password_hash)
|
||||
assert not verify_password("original-password", refreshed.password_hash)
|
||||
# B-34: the reset must bump token_version so a session opened before
|
||||
# the reset (e.g. an attacker who had the old password) is evicted
|
||||
# immediately rather than staying valid until the JWT naturally expires.
|
||||
assert refreshed.token_version == 1
|
||||
|
||||
|
||||
async def test_admin_reset_password_requires_token(client):
|
||||
|
||||
Reference in New Issue
Block a user