Reset tokens live only in Redis as a paired key shape (mirrors 010's own revocation-denylist pattern) - never in Postgres, never storing the raw token. Password-strength policy is one shared validator called from both the new reset-consume endpoint and 010's existing POST /admin/users. Login rate-limiting reuses the existing checkRateLimit helper from 002's own inbound trust boundary, keyed by submitted email, checked before any credential verification. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2.0 KiB
2.0 KiB
Quickstart: Validating Authentication Hardening
Scenario 1 — password reset, end to end
POST /auth/password-reset/requestwith a real seeded account's email. Expected:200, generic message; the server log shows apassword_reset_requestedline with aresetUrlcontaining the real token.- Repeat with an email that doesn't exist. Expected: identical
200response body to step 1 — diff them to confirm. POST /auth/password-reset/consumewith the token from step 1's log and a policy-meeting new password. Expected:200.- Repeat step 3 with the same token. Expected: rejected — the token is single-use.
POST /auth/loginwith the account's email and the new password from step 3. Expected:200. Repeat with the account's old password. Expected:401.
Scenario 2 — password strength enforced everywhere
POST /admin/users(as admin) with a password shorter thanPASSWORD_MIN_LENGTH. Expected:400, naming the actual minimum length.POST /auth/password-reset/consumewith a valid token and a too-short new password. Expected: the same400rejection reason as step 1.
Scenario 3 — login rate limiting
- Submit
LOGIN_RATE_LIMIT_MAX_ATTEMPTSfailed login attempts for the same email withinLOGIN_RATE_LIMIT_WINDOW_SECONDS. Expected: each returns401(the existing identical-failure-response). - Submit one more attempt for that same email, still within the window — this time with the
correct password. Expected:
429, not200— the rate limit is checked before credentials (FR-007). - Submit an attempt for a different email within the same window. Expected: proceeds normally (evaluated on its own credentials, not rate-limited).
What "done" looks like
All three scenarios pass against a real Postgres/Redis, and POST /admin/users's own existing
tests (010-identity-auth) still pass with the added password-strength check in place.