Files
support_backend/specs/013-auth-hardening/quickstart.md
T
saqib mirandClaude Sonnet 5 52f1fa3db0 docs(013-auth-hardening): plan, research, data model, contract, quickstart
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>
2026-09-07 17:26:49 +05:30

2.0 KiB

Quickstart: Validating Authentication Hardening

Scenario 1 — password reset, end to end

  1. POST /auth/password-reset/request with a real seeded account's email. Expected: 200, generic message; the server log shows a password_reset_requested line with a resetUrl containing the real token.
  2. Repeat with an email that doesn't exist. Expected: identical 200 response body to step 1 — diff them to confirm.
  3. POST /auth/password-reset/consume with the token from step 1's log and a policy-meeting new password. Expected: 200.
  4. Repeat step 3 with the same token. Expected: rejected — the token is single-use.
  5. POST /auth/login with 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

  1. POST /admin/users (as admin) with a password shorter than PASSWORD_MIN_LENGTH. Expected: 400, naming the actual minimum length.
  2. POST /auth/password-reset/consume with a valid token and a too-short new password. Expected: the same 400 rejection reason as step 1.

Scenario 3 — login rate limiting

  1. Submit LOGIN_RATE_LIMIT_MAX_ATTEMPTS failed login attempts for the same email within LOGIN_RATE_LIMIT_WINDOW_SECONDS. Expected: each returns 401 (the existing identical-failure-response).
  2. Submit one more attempt for that same email, still within the window — this time with the correct password. Expected: 429, not 200 — the rate limit is checked before credentials (FR-007).
  3. 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.