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