Rate Limiting
Throttling auth endpoints against credential stuffing and abuse.
Nest Auth ships built-in rate limiting, soft account lockout, and a CAPTCHA hook for its sensitive endpoints. They're opt-in (default off, no behavior change until enabled) — except the admin console, which is brute-force-protected by default (see Admin Console → Security). You can also bring @nestjs/throttler alongside or instead (below).
Built-in rate limiting (security.rateLimit)
Turn it on and the library throttles its own login / signup / forgot-password / passwordless-send / verify-otp / mfa-verify / admin-login routes — no extra controllers, and it runs ahead of NestAuthAuthGuard so bots are rejected before any auth work:
Exceeding a bucket returns 429 with a Retry-After header and code: 'RATE_LIMITED'. In-memory counters are per-process — supply a shared store for multi-instance deployments.
Built-in soft lockout (security.lockout)
Complements rate limiting: after N failed logins for an identifier+IP, further attempts are refused for a cooldown (a longer, softer window than the per-minute rate limit). It's event-driven (counts LOGIN_FAILED, clears on LOGGED_IN) and now covers admin login too:
A locked identifier gets 429 + code: 'ACCOUNT_LOCKED'. Keyed by identifier and IP so an attacker can't lock a victim out from another IP.
Alternative: @nestjs/throttler
You can still bring your own edge throttler (e.g. to cap total request volume before auth logic runs), on its own or in addition to the built-ins:
The throttler guard runs ahead of NestAuthAuthGuard, so unauthenticated bots are rejected before any auth work happens.
Per-endpoint thresholds
Override the default for high-risk endpoints with @Throttle():
Recommended starting points:
| Endpoint | Window | Limit |
|---|---|---|
/auth/login | 60s | 5 per IP |
/auth/signup | 60s | 3 per IP |
/auth/passwordless/send | 60s | 3 per identifier |
/auth/forgot-password | 60s | 3 per identifier |
/auth/mfa/challenge | 60s | 5 per session |
/auth/refresh-token | 60s | 60 per session — high, since auto-refresh happens often |
/auth/reset-password | 60s | 5 per IP |
Tune from there using your own auth-failure dashboard.
Per-identifier vs per-IP
Per-IP alone is insufficient — an attacker behind a CDN looks like one IP. Per-identifier (email/phone) protects the targeted user. The strongest setup limits both:
- Per-IP: blocks distributed brute force from one source.
- Per-identifier: blocks credential stuffing against one account.
@nestjs/throttler v6+ supports custom trackers. Use a key that combines both:
Soft vs hard limits
For login specifically:
- Hard limit — return 429 after N attempts. Protects the system.
- Soft limit — silently delay the response after N attempts. Slows attackers without revealing the threshold.
A common combination: instant lockout after 5 failures within 15 minutes, with a captcha challenge on the 4th attempt.
Storing counters
In-memory works for a single instance. For multi-instance, use the Redis storage adapter for @nestjs/throttler so the counters are shared.
Audit + telemetry
Wire your throttler's "limit exceeded" event into your audit hook so you have a record of every block. Spikes are a leading indicator of an in-progress attack.