fix(rate-limit): stop double-counting requests in middleware

Multiple RateLimitMiddleware instances were each calling
check_allowed() on every request, halving the effective global
limit (200 req/min became ~100). Added path_prefixes and skip_paths
so each instance only checks the paths it owns.

- Auth middleware scoped to /api/v1/auth/login and /api/v1/setup
- History middleware scoped to /api/v1/history
- Global middleware skips auth and history paths
- Updated tests to match single-count behavior
This commit is contained in:
2026-05-15 23:04:02 +02:00
parent 77df5d5d65
commit 7308ff88d6
3 changed files with 92 additions and 45 deletions

View File

@@ -1135,9 +1135,7 @@ def create_app(settings: Settings | None = None) -> FastAPI:
app.add_middleware(CsrfMiddleware)
app.add_middleware(DeprecationHeaderMiddleware)
# Auth endpoints (login, setup) need a dedicated higher-rate bucket to avoid
# rate limiting when running e2e tests sequentially. Auth uses the default
# global rate limiter at 200 req/min per IP.
# Auth endpoints: /api/v1/login, /api/v1/setup
# rate limiting when running e2e tests sequentially.
# 1000 req/min per IP — generous for e2e testing.
app.add_middleware(
RateLimitMiddleware,
@@ -1146,6 +1144,7 @@ def create_app(settings: Settings | None = None) -> FastAPI:
bucket_override="auth:login",
bucket_max_requests=1000,
bucket_window_seconds=60,
path_prefixes=["/api/v1/auth/login", "/api/v1/setup"],
)
# History endpoints get a dedicated higher-rate bucket to avoid
@@ -1159,6 +1158,16 @@ def create_app(settings: Settings | None = None) -> FastAPI:
bucket_override="history:list",
bucket_max_requests=10000,
bucket_window_seconds=60,
path_prefixes=["/api/v1/history"],
)
# Global rate limiter for all other endpoints.
# 200 req/min per IP — default protection.
app.add_middleware(
RateLimitMiddleware,
rate_limiter=app.state.global_rate_limiter,
settings=resolved_settings,
skip_paths=["/api/v1/auth/login", "/api/v1/setup", "/api/v1/history"],
)
# Validate middleware order before returning the app.