[GH-ISSUE #7706] Login rate limit (5/15min) breaks API clients that reconnect per-operation (actual-cli, scripting) #72914

Closed
opened 2026-05-16 13:50:37 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @danielhopkins on GitHub (May 4, 2026).
Original GitHub issue: https://github.com/actualbudget/actual/issues/7706

PR #7432 added a hardcoded rate limit of 5 logins per 15 minutes on /account/login. This is reasonable protection against brute-force attacks from untrusted sources, but it creates a significant problem for legitimate API clients that authenticate per-operation rather than maintaining a persistent session.

The problem: actual-cli (and any client built on @actual-app/api that initializes a new connection per call) performs a fresh login for every invocation. A script running 6+ sequential CLI commands will exhaust the quota, returning {"status":"error","reason":"too-many-requests"} and locking out all clients — including the MCP server and the web UI — until the 15-minute window resets.

Workaround: Restart the server container to clear the in-memory rate limit state, then prefer long-lived clients (like actual-mcp) that maintain a persistent session.

Suggested fixes:

  1. Only count failed attempts — This is the conventional approach for brute-force protection. An attacker cycling through passwords generates many failures; a legitimate client with the correct password succeeds immediately and shouldn't consume quota at all. express-rate-limit supports this natively via skipSuccessfulRequests: true. With this change, scripted clients are completely unaffected while brute-force attempts still hit the wall.

  2. Make the limit configurable — Expose the window and max via environment variables (e.g., AUTH_RATE_LIMIT_MAX, AUTH_RATE_LIMIT_WINDOW_MS) so self-hosted single-user instances can raise or disable the limit. The current hardcoded value suits public-facing multi-user deployments but is too restrictive for personal home servers with scripted workflows.

Related: #7432 (comment by @2manyvcos raises the same concern)

Originally created by @danielhopkins on GitHub (May 4, 2026). Original GitHub issue: https://github.com/actualbudget/actual/issues/7706 PR #7432 added a hardcoded rate limit of 5 logins per 15 minutes on `/account/login`. This is reasonable protection against brute-force attacks from untrusted sources, but it creates a significant problem for legitimate API clients that authenticate per-operation rather than maintaining a persistent session. **The problem:** `actual-cli` (and any client built on `@actual-app/api` that initializes a new connection per call) performs a fresh login for every invocation. A script running 6+ sequential CLI commands will exhaust the quota, returning `{"status":"error","reason":"too-many-requests"}` and locking out *all* clients — including the MCP server and the web UI — until the 15-minute window resets. **Workaround:** Restart the server container to clear the in-memory rate limit state, then prefer long-lived clients (like `actual-mcp`) that maintain a persistent session. **Suggested fixes:** 1. **Only count failed attempts** — This is the conventional approach for brute-force protection. An attacker cycling through passwords generates many failures; a legitimate client with the correct password succeeds immediately and shouldn't consume quota at all. `express-rate-limit` supports this natively via `skipSuccessfulRequests: true`. With this change, scripted clients are completely unaffected while brute-force attempts still hit the wall. 2. **Make the limit configurable** — Expose the window and max via environment variables (e.g., `AUTH_RATE_LIMIT_MAX`, `AUTH_RATE_LIMIT_WINDOW_MS`) so self-hosted single-user instances can raise or disable the limit. The current hardcoded value suits public-facing multi-user deployments but is too restrictive for personal home servers with scripted workflows. Related: #7432 (comment by @2manyvcos raises the same concern)
Author
Owner

@danielhopkins commented on GitHub (May 4, 2026):

PR submitted: #7707 — one-line fix adding skipSuccessfulRequests: true to the rate limiter.

<!-- gh-comment-id:4372137722 --> @danielhopkins commented on GitHub (May 4, 2026): PR submitted: #7707 — one-line fix adding `skipSuccessfulRequests: true` to the rate limiter.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/actual#72914