Rate Limiting

Implemented AuthOS request limits, client-IP trust, email throttles, and safe retry behavior.

AuthOS release 0.8.5 API v1 Latest-only documentation
Updated Aug 2, 2026
On this page

AuthOS applies in-process token-bucket limits to the most abuse-sensitive route groups. Limits are keyed by the trusted client IP derived by TrustedClientIpKeyExtractor; configure trusted proxy handling correctly before placing the API behind a load balancer. Do not trust arbitrary forwarded headers from direct clients.

Route-group limits

Group Routes Refill interval Burst
Authentication /auth/* and public /api/auth/* routes assembled by auth_routes 1 second 20
Device authorization /auth/device/code, /auth/device/verify, /auth/token 5 seconds 10
Authenticated MFA management /api/user/mfa/status, /setup, /verify, disable, backup-code regeneration 300 ms 5
Login/SAML MFA verification /api/auth/mfa/verify, /saml/mfa/verify 1 second 3

These values are source defaults, not a quota promise for every deployment. Operators can disable the route layers with DISABLE_RATE_LIMITING=true for isolated tests; never do that on an internet-facing deployment.

Email-generating password, verification, and magic-link handlers also use a separate in-memory throttle: at most five attempts per hour for the normalized email key, partitioned by organization where tenant context is available. MFA middleware also defines a five-attempt, 15-minute IP/user limiter for flows that attach it.

Handling 429

Treat 429 Too Many Requests as temporary. Honor Retry-After when present; otherwise use bounded exponential backoff with jitter. Retry only idempotent reads or operations whose idempotency you control—blindly replaying invitation, token, or billing writes can duplicate work.

async function getWithBackoff(url, options = {}, attempts = 3) {
  for (let attempt = 0; attempt < attempts; attempt += 1) {
    const response = await fetch(url, options);
    if (response.status !== 429) return response;

    const retryAfter = Number(response.headers.get('retry-after'));
    const baseMs = Number.isFinite(retryAfter)
      ? retryAfter * 1000
      : Math.min(30_000, 500 * 2 ** attempt);
    const jitterMs = Math.floor(Math.random() * 250);
    await new Promise(resolve => setTimeout(resolve, baseMs + jitterMs));
  }
  throw new Error('AuthOS rate limit retry budget exhausted');
}

Keep concurrency bounded, cache safe reads, and prefer signed webhooks over polling. Because the built-in counters are process-local, multi-replica deployments must add an edge or shared distributed limiter when they require a strict platform-wide ceiling.

Monitor 429 rates without logging passwords, authorization headers, one-time tokens, or request bodies containing secrets.