fix(auth): cap + anti-replay TOTP verification on agent login #400
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/PMS-502-mfa-totp-attempt-cap-anti-replay"
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?
The legacy agent login MFA path verified the TOTP code with
totp::verifybut discarded the matched step and kept no per-account attempt accounting. The
only throttle was the in-memory per-email login limiter (5/min), which resets
on restart, does not coordinate across replicas, and is shared with password
attempts. With a +/-1 window over a 6-digit space (3 of 1,000,000 live codes at
any instant) that let an attacker holding the password grind second-factor
codes until one matched.
Persist per-account second-factor state on the
usersrow (migration 084):mfa_last_used_step(anti-replay watermark),mfa_failed_attempts, andmfa_locked_until. The login MFA branch now reads this state, rejects with 429 while a lockout window is active, only honours a TOTP step strictly greater than the last accepted one (so a captured code cannot be replayed inside its window), and on a wrong or replayed code increments the counter and arms an exponential-backoff lockout (mfa_lockout_until: none under 3 failures, then 30s doubling to a 1h cap). A successful code advances the watermark and clears the counter + lockout. This second-factor defence is persistent and independent of, and stricter than, the in-memory password-login bucket; it mirrors the portal-login remediation (PMS-501, migration 082).Tests: unit tests pin the lockout schedule; integration tests pin TOTP replay rejection and that repeated wrong codes arm a persistent lockout that rejects even a correct code with 429.
#PMS-502