--- issue: 054 title: "Login rate limiter counts all attempts (not just failures) — users locked out after correct logins" severity: major domain: Authentication status: resolved resolved: 2026-05-29 fix: "Split checkLoginAttempts into read-only check and new incrementFailedLoginAttempt. authController now only calls increment on failed login paths, not on all attempts." labels: [backend, bug] status: open created: 2026-05-29 source: Doc vs Code Audit 2026-05-29 --- # 🟠 Login rate limiter counts all attempts (not just failures) — users locked out after correct logins **Severity:** major **Domain:** Authentication **Labels:** backend, bug ## Description `rateLimitService.checkLoginAttempts()` calls `checkLimit()` → `redisService.incr`, incrementing the counter on **every** login invocation, before password comparison. The counter only resets after a fully successful login. So 5 total attempts within 15 min (any mix of correct/incorrect passwords) triggers the lockout — not 5 failures as the docs imply. ## Current Behavior 5 total login attempts within 15 minutes → `429 TOO_MANY_ATTEMPTS`, even if some attempts used the correct password. ## Expected Behavior The counter should increment only on a **failed** password comparison, not on every attempt. Otherwise document the actual behaviour so UX warns users appropriately. ## Affected Files - `backend/src/services/auth/rateLimitService.ts` — `checkLoginAttempts` / `checkLimit` - `backend/src/controllers/authController.ts` — move the increment to after password comparison ## References - [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M3