# 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.