Rate limits
Default budgets, per-route overrides, brute-force protection, and how to handle 429s.
The Auth API rate-limits requests with @fastify/rate-limit. The defaults are
generous for normal usage and aggressive on the routes attackers care about.
Default budget
| Scope | Budget |
|---|---|
| Global | 100 req/min per IP |
POST /login | 5 req/min per IP |
Other routes inherit the global budget unless overridden in the service config. Authenticated and unauthenticated traffic both count against the same per-IP bucket.
Hitting the limit
HTTP/1.1 429 Too Many Requests
Retry-After: 42{ "success": false, "error": "Rate limit exceeded", "code": "RATE_LIMIT_EXCEEDED" }Retry-After is in seconds. Clients should wait at least that long before
retrying — anything faster will be denied again.
Why login is so tight
Five requests per minute is enough for legitimate retries (typo, wrong caps lock) and far below what's needed to brute-force a typical password. It combines with the dummy-bcrypt timing-attack mitigation and the tenant security metrics to make password attacks expensive and noisy.
Brute-force detection
The Auth API exposes two metric endpoints that surface attack patterns:
GET /api/v1/tenants/me/metrics/securityReturns:
- 24h login summary — total, successful, failed, success rate.
- 5-minute window — failed attempts and unique IPs.
- IP analysis — top offenders, IPs with ≥ 5 failures in 24 hours flagged as suspicious.
The threshold for "possible brute force" is >10 failures in 5 minutes. Wire alerts on top of these numbers — the Auth API does not page you, but it gives you the inputs to do so.
Distributed deployments
The default @fastify/rate-limit store is in-memory per process. For a
multi-instance deployment behind a load balancer, you have two choices:
- Sticky sessions — same IP always lands on the same process. Cheap, keeps the in-memory store correct.
- Redis store — switch the limiter to Redis-backed. The Auth API ships
with
ioredisandBullMQalready installed; the wiring is a small config change insrc/server.ts.
For high-volume tenants, the platform team can lift the budget per-tenant. Open a ticket with traffic projections and the desired budget.
Trust proxy
The service runs with trustProxy: true, so it honors X-Forwarded-For
when the request arrives via a reverse proxy. Make sure your proxy strips
or sanitizes that header for untrusted clients — the Auth API trusts what
it sees.
What clients should do
Honor Retry-After
Always. It's the minimum safe wait. Don't add fixed jitter on top — the server is already telling you the right answer.
Backoff on repeated 429s
If two consecutive Retry-Afters are needed, your traffic shape is the
problem. Drop to a fraction of your usual rate.
Separate keys per integration
For Client Keys, isolate workloads. A misbehaving exporter shouldn't starve the production traffic that shares its key.
Watch the security metrics
For tenants you operate, periodically poll
/tenants/me/metrics/security. The 5-minute window is the most useful
real-time signal.