Problem
Account password sign-in currently verifies administrator-managed passwords through POST /account/session. The latest password policy work hardens password creation, hash format, and timing behavior for missing credentials, but wrong-password retries are still not throttled or cooled down explicitly.
Because password verification uses Argon2id, repeated sign-in attempts can amplify CPU and memory cost. Repeated attempts against a real account can also enable password guessing unless the sign-in boundary applies request throttling or failure-aware cooldown.
Current state
- Managed password creation is restricted to printable ASCII, 8 to 128 bytes, with no leading or trailing spaces.
- Password sign-in accepts the submitted password verbatim up to 128 bytes for existing credential compatibility.
- Password verification uses the fixed Account Argon2id profile.
- Unknown users and users without password credentials run dummy Argon2id verification before returning invalid sign-in.
- There is no login-specific failed-attempt counter, cooldown, account lockout,
429, or Retry-After.
- The existing Redis sliding-window limiter in
internal/ratelimit may be reusable for simple request throttling.
Proposed scope
Add abuse protection to POST /account/session.
Initial scope should prefer lightweight Redis-backed throttling:
- Limit by source IP.
- Limit by username plus source IP.
- Return
429 Too Many Requests with Retry-After when throttled.
- Keep ordinary invalid credentials as
401 Unauthorized.
- Avoid exposing whether the username exists or has a password credential.
- Keep the protection compatible with hosted sign-in and Account Web callers.
Candidate behavior
- Check throttling before password verification so denied bursts do not trigger unbounded Argon2id work.
- Record attempts regardless of whether the username exists, so username enumeration does not get a cheaper path.
- Treat username comparisons consistently with the existing sign-in path.
- Clear or naturally expire attempt state through Redis TTLs rather than adding account-level persistent lock state.
- Use conservative thresholds first, then tune from production signals if needed.
Acceptance criteria
- Password sign-in attempts are throttled before unbounded Argon2id work can be triggered.
- Throttled responses include
Retry-After.
- Invalid credentials remain indistinguishable across unknown user, missing password credential, and wrong password.
- OpenAPI documents the new
429 response for POST /account/session.
- Product docs describe password sign-in abuse protection at a high level.
- Backend tests cover allowed, invalid, and throttled sign-in attempts.
- Frontend Account Web behavior handles the throttled response without treating it as a generic credential failure if applicable.
Non-goals
- CAPTCHA.
- Device fingerprinting.
- Risk scoring.
- Permanent account lockout.
- Administrator unlock workflow.
- Self-service password reset.
- Changing the managed password policy.
- Changing the Argon2id profile.
- Reworking third-party identity provider sign-in.
Notes
A failure-aware cooldown can be considered after the basic request throttle is in place. Username-only lockout should be avoided initially because it can let attackers lock other users out.
Problem
Account password sign-in currently verifies administrator-managed passwords through
POST /account/session. The latest password policy work hardens password creation, hash format, and timing behavior for missing credentials, but wrong-password retries are still not throttled or cooled down explicitly.Because password verification uses Argon2id, repeated sign-in attempts can amplify CPU and memory cost. Repeated attempts against a real account can also enable password guessing unless the sign-in boundary applies request throttling or failure-aware cooldown.
Current state
429, orRetry-After.internal/ratelimitmay be reusable for simple request throttling.Proposed scope
Add abuse protection to
POST /account/session.Initial scope should prefer lightweight Redis-backed throttling:
429 Too Many RequestswithRetry-Afterwhen throttled.401 Unauthorized.Candidate behavior
Acceptance criteria
Retry-After.429response forPOST /account/session.Non-goals
Notes
A failure-aware cooldown can be considered after the basic request throttle is in place. Username-only lockout should be avoided initially because it can let attackers lock other users out.