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
- Start the web app with local authentication enabled.
- POST repeated invalid credentials to
/api/auth/login, for example the same known username with different passwords.
- 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
Evidence
apps/web/src/app/api/auth/login/route.tsaccepts every POST body with a username and password, callsensureLegacySharedUser(), then callsverifyCredentials()without any per-client or per-account throttling.apps/web/src/lib/auth.tsperforms 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:12throughapps/web/src/app/api/auth/login/route.ts:25apps/web/src/lib/auth.ts:203throughapps/web/src/lib/auth.ts:223Impact
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
/api/auth/login, for example the same known username with different passwords.401 Invalid credentialswith no429response or cooldown once failures accumulate.Suggested fix
Add a small server-side rate limiter for authentication attempts. A minimal durable fix should:
429with a generic message andRetry-Afterwhen throttled;Responsible Agent
Developer