feat(auth): invalidate access tokens on password reset/change (PMS-681) #460
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/PMS-681-invalidate-tokens-on-password-change"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Closes PMS-681: a password reset revoked refresh sessions but left the already-issued access token valid until its 1h TTL (the access path is stateless). Now a reset - and a self-service change - invalidate every already-issued access token immediately.
How
users.password_changed_at(migration093_users_password_changed_at.sql), stamped toNOW()byreset_passwordandchange_password.iatpredatespassword_changed_at. The check is folded into the per-requestensure_user_and_tenant_active(the JWT already carriesiat, and the users row is already read per request, so this adds one PK-scoped scalar read under the tenant GUC). A NULL stamp means no cutoff, so tokens in flight survive the deploy. A failed check maps to unauthenticated -> 401, exactly like the existing "account not active" path.logout_all(refresh revoked); the stamp now kills the access token immediately too, so the user is fully logged out at once.change_password (decided with Nate: log out everywhere)
change_passwordpreviously revoked nothing. It now stampspassword_changed_atand callslogout_all, so after a self-service password change every session drops (including the current device) and the user signs in again.Tests
password_changed_atis stamped.access_token_rejected_after_password_change: a token issued before the stamp gets 401 (stamps a future instant for determinism).change_password_logs_out_everywhere: the change revokes all sessions and stampspassword_changed_at; the old password fails, the new one works.cargo test --test authpasses against Postgres;cargo check --all-targetsand clippy are clean.Notes
Second granularity: a token minted in the same wall-clock second as the change is not rejected (strict
iat <comparison). Negligible for the threat model (a stolen token was minted seconds to hours before the reset), and it avoids invalidating a legitimate new login issued in that same second.#PMS-681