Skip to content

Developer: login endpoint lacks failed-attempt rate limiting #48

@DeliciousBuding

Description

@DeliciousBuding

Evidence

apps/web/src/app/api/auth/login/route.ts accepts every POST body with a username and password, calls ensureLegacySharedUser(), then calls verifyCredentials() without any per-client or per-account throttling.

apps/web/src/lib/auth.ts performs a bcrypt comparison for an existing password hash on each matching username or email. The repository does not currently expose an application-level rate limit or lockout for this public endpoint.

Relevant code locations:

  • apps/web/src/app/api/auth/login/route.ts:12 through apps/web/src/app/api/auth/login/route.ts:25
  • apps/web/src/lib/auth.ts:203 through apps/web/src/lib/auth.ts:223

Impact

An unauthenticated client can repeatedly submit passwords for the same account and force bcrypt work on the web process. This enables online password guessing and gives a low-cost request path that can consume CPU. Reverse proxies can still add coarse throttling, but the product-facing auth boundary should have an application-level guard so self-hosted deployments are not relying on undocumented edge behavior.

Reproduction

  1. Start the web app with local authentication enabled.
  2. POST repeated invalid credentials to /api/auth/login, for example the same known username with different passwords.
  3. Observe each request reaches the credential verifier and returns 401 Invalid credentials with no 429 response or cooldown once failures accumulate.

Suggested fix

Add a small server-side rate limiter for authentication attempts. A minimal durable fix should:

  • throttle by client identity and normalized login identifier before bcrypt verification;
  • count only failed authentication attempts;
  • reset the counter on successful login;
  • return 429 with a generic message and Retry-After when throttled;
  • keep behavior deterministic under tests and avoid leaking whether a username exists.

Responsible Agent

Developer

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions