Rate Limiting
Auris applica rate limit su tutti gli endpoint API per proteggere la piattaforma dagli abusi, prevenire attacchi di credential stuffing e garantire un’allocazione equa delle risorse tra i tenant. Questa guida spiega i livelli di rate limiting, come leggere gli header dei limiti, come gestire le risposte 429 Too Many Requests nella tua applicazione e come configurare i limiti.
Livelli di Rate Limiting
Auris applica rate limit diversi in base alla sensibilità e al costo delle risorse di ciascun endpoint. Ci sono quattro livelli:
Livello Auth (Rigoroso)
Si applica agli endpoint di autenticazione che gestiscono le credenziali:
POST /api/auth/login(login email/password)POST /api/auth/signup(registrazione utente)POST /api/auth/token(scambio e refresh token)POST /api/auth/magic-link(avvio magic link)POST /api/auth/passwordless/*(flussi passwordless)POST /api/oauth/authenticate(invio form login ospitato)
Questi endpoint hanno i limiti più restrittivi perché sono il bersaglio principale degli attacchi di credential stuffing e brute-force.
Limiti predefiniti: Poche richieste al minuto per IP, con limiti aggiuntivi per account sugli endpoint di login.
Livello Sensitive (Moderato)
Si applica alle operazioni critiche per la sicurezza che non dovrebbero essere chiamate frequentemente:
POST /api/user/2fa/*(iscrizione e verifica 2FA)POST /api/auth/forgot-password(avvio reset password)POST /api/auth/change-password(cambio password)POST /api/user/phone/*(verifica numero di telefono)
Limiti predefiniti: Richieste moderate al minuto per IP e per utente.
Livello API (Standard)
Si applica agli endpoint API autenticati generali:
GET/POST/PATCH/DELETE /api/users/*GET/POST/PATCH/DELETE /api/roles/*GET/POST/PATCH/DELETE /api/organizations/*GET/POST/PATCH/DELETE /api/applications/*- Tutti gli altri endpoint CRUD autenticati
Limiti predefiniti: Richieste standard al minuto per utente autenticato.
Livello Public (Rilassato)
Si applica agli endpoint aperti che non richiedono autenticazione:
GET /.well-known/openid-configuration(OIDC Discovery)GET /.well-known/jwks.json(JWKS)GET /api/public/*(pagine di stato pubbliche, pagine di verifica)POST /api/scim/v2/*(provisioning SCIM con bearer token)
Limiti predefiniti: Limiti generosi, solo basati su IP.
Lettura degli Header dei Rate Limit
Ogni risposta da un endpoint con rate limit include header standard che indicano lo stato corrente della finestra di rate limit:
| Header | Tipo | Descrizione |
|---|---|---|
X-RateLimit-Limit | Intero | Numero massimo di richieste consentite nella finestra corrente |
X-RateLimit-Remaining | Intero | Numero di richieste rimanenti prima di raggiungere il limite |
X-RateLimit-Reset | Unix timestamp | Quando si resetta la finestra corrente (secondi dall’epoch) |
Retry-After | Intero | Secondi prima di poter riprovare (presente solo nelle risposte 429) |
Esempio di header di risposta su una richiesta riuscita:
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1737014400
Content-Type: application/jsonEsempio di header quando il rate limit è raggiunto:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1737014400
Retry-After: 47
Content-Type: application/json
{
"ok": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Troppe richieste. Riprova tra 47 secondi."
}
}Gestione delle Risposte 429
Quando la tua applicazione riceve una risposta 429 Too Many Requests, dovrebbe fare backoff e riprovare dopo il ritardo specificato nell’header Retry-After.
Retry di Base con Backoff
async function callAurisApi(
url: string,
options: RequestInit,
maxRetries = 3
): Promise<Response> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options)
if (response.status !== 429) {
return response
}
// Leggi l'header Retry-After
const retryAfter = parseInt(response.headers.get('Retry-After') || '60', 10)
if (attempt === maxRetries) {
throw new Error(
`Rate limited dopo ${maxRetries} tentativi. Riprova tra ${retryAfter}s.`
)
}
console.warn(
`Rate limited (tentativo ${attempt + 1}/${maxRetries}). ` +
`Riprovo tra ${retryAfter} secondi...`
)
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000))
}
throw new Error('Inatteso: loop di retry esaurito')
}Backoff Esponenziale con Jitter
Per sistemi di produzione ad alto traffico, usa il backoff esponenziale con jitter per evitare problemi di “thundering herd” quando molti client raggiungono i limiti contemporaneamente:
async function callWithExponentialBackoff(
url: string,
options: RequestInit,
maxRetries = 5
): Promise<Response> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options)
if (response.status !== 429) {
return response
}
if (attempt === maxRetries) {
throw new Error('Rate limit superato: tentativi massimi raggiunti')
}
const retryAfter = response.headers.get('Retry-After')
let delay: number
if (retryAfter) {
delay = parseInt(retryAfter, 10) * 1000
} else {
// Backoff esponenziale: 1s, 2s, 4s, 8s, 16s
const baseDelay = Math.pow(2, attempt) * 1000
const jitter = Math.random() * baseDelay * 0.5
delay = baseDelay + jitter
}
await new Promise((resolve) => setTimeout(resolve, delay))
}
throw new Error('Inatteso: loop di retry esaurito')
}Pattern Circuit Breaker
Per applicazioni che effettuano molte chiamate API, implementa un circuit breaker per smettere del tutto di inviare richieste quando i rate limit vengono colpiti consistentemente:
class AurisCircuitBreaker {
private failureCount = 0
private lastFailure = 0
private state: 'closed' | 'open' | 'half-open' = 'closed'
private readonly threshold = 5
private readonly resetTimeout = 60000 // 1 minuto
async call(fn: () => Promise<Response>): Promise<Response> {
if (this.state === 'open') {
if (Date.now() - this.lastFailure > this.resetTimeout) {
this.state = 'half-open'
} else {
throw new Error('Circuit breaker aperto. Richieste in pausa.')
}
}
const response = await fn()
if (response.status === 429) {
this.failureCount++
this.lastFailure = Date.now()
if (this.failureCount >= this.threshold) {
this.state = 'open'
console.error(
`Circuit breaker aperto dopo ${this.threshold} rate limit colpiti. ` +
`Pausa richieste per ${this.resetTimeout / 1000}s.`
)
}
throw new Error('Rate limited')
}
// Successo — resetta il breaker
this.failureCount = 0
this.state = 'closed'
return response
}
}Limiti Per Account sugli Endpoint Auth
In aggiunta ai rate limit basati su IP, gli endpoint di autenticazione applicano limiti per account che funzionano in combinazione con il sistema di protezione brute-force:
| Soglia | Azione |
|---|---|
| Login falliti consecutivi nella finestra di osservazione | Contatore incrementato |
| Il contatore raggiunge la soglia di lockout (default: 5) | Account bloccato per durata crescente |
| 1° lockout | 5 minuti |
| 2° lockout | 30 minuti |
| 3° lockout | 24 ore |
Questi limiti sono per account utente e persistono tra indirizzi IP. Un attacco distribuito da più IP contro lo stesso account attiva comunque il lockout.
Il lockout per account è separato dal rate limiting basato su IP. Un singolo IP può essere rate-limited mentre l’account target rimane sbloccato (se il contatore fallimenti non ha raggiunto la soglia), e viceversa.
Vedi la guida Protezione dagli Attacchi per i dettagli completi sulla configurazione della protezione brute-force.
Comportamento di Retry Automatico dell’SDK
L’SDK @auris/js include gestione integrata dei rate limit per le operazioni sui token:
- Refresh token: Se una richiesta di refresh token riceve un 429, l’SDK attende la durata
Retry-Aftere riprova una volta automaticamente - Redirect di login: Il flusso di login ospitato è server-rendered e non soggetto ai rate limit lato client
- Chiamate API tramite client Management: Il client Management non riprova automaticamente — la tua applicazione dovrebbe implementare la logica di retry
import { AurisClient } from '@auris/js'
const auris = new AurisClient({
domain: 'auth.tuazienda.com',
clientId: 'tuo-client-id',
autoRefresh: true, // L'SDK gestisce il refresh con retry integrato
})
// Il retry del refresh token è automatico
const token = await auris.getAccessToken()Per il client Management (M2M), implementa la tua logica di retry:
import { AurisClient } from '@auris/js'
const management = new AurisClient.Management({
domain: 'auth.tuazienda.com',
clientId: 'm2m-client-id',
clientSecret: 'm2m-client-secret',
})
// Le chiamate al client Management dovrebbero usare il tuo wrapper di retry
const users = await callWithExponentialBackoff(
'https://auth.tuazienda.com/api/users',
{
headers: {
Authorization: `Bearer ${await management.getToken()}`,
'x-tenant': 'tuo-tenant-id',
},
}
)Configurazione nella Console
Le impostazioni di rate limiting sono visualizzabili nella Console Auris sotto Impostazioni poi Rate Limiting. La Console mostra la configurazione corrente per ciascun livello in sola lettura.
Le impostazioni mostrate includono:
- Limiti per livello — Richieste per finestra per ciascun livello (auth, sensitive, api, public)
- Durata della finestra — La dimensione della sliding window per ciascun livello
- Stato corrente — Se il rate limiting è attivo
I valori dei rate limit sono configurati a livello infrastrutturale e sono mostrati nella Console per riferimento. Per richiedere modifiche alle soglie di rate limit per il tuo tenant, contatta il tuo amministratore Auris o modifica la configurazione dell’ambiente.
Monitoraggio Proattivo dei Rate Limit
Invece di aspettare le risposte 429, monitora proattivamente l’header X-RateLimit-Remaining per limitare il tuo tasso di richieste prima di raggiungere i limiti:
class RateLimitAwareClient {
private remaining = Infinity
private resetAt = 0
async request(url: string, options: RequestInit): Promise<Response> {
// Se sappiamo di essere al limite, attendi in modo proattivo
if (this.remaining <= 1 && Date.now() / 1000 < this.resetAt) {
const waitMs = (this.resetAt - Date.now() / 1000) * 1000
console.log(`Attesa proattiva di ${waitMs}ms per evitare il rate limit`)
await new Promise((resolve) => setTimeout(resolve, waitMs))
}
const response = await fetch(url, options)
// Aggiorna lo stato del rate limit dagli header della risposta
const limit = response.headers.get('X-RateLimit-Remaining')
const reset = response.headers.get('X-RateLimit-Reset')
if (limit !== null) this.remaining = parseInt(limit, 10)
if (reset !== null) this.resetAt = parseInt(reset, 10)
return response
}
}Best Practice
Rispetta sempre Retry-After. Quando ricevi un 429, l’header Retry-After ti dice esattamente quanto tempo aspettare. Non riprovare prima di questo periodo.
Usa il backoff esponenziale con jitter. Per operazioni bulk o scenari ad alto throughput, il retry lineare semplice può causare picchi di traffico quando più client riprovano nello stesso momento. Il jitter distribuisce i retry nel tempo.
Implementa circuit breaker per i percorsi critici. Se la tua applicazione dipende dalle chiamate API Auris nel percorso delle richieste (es. controlli dei permessi), usa un circuit breaker per fallire in modo elegante invece di accodare le richieste mentre è in rate limiting.
Cache le risposte dove possibile. Riduci le chiamate API memorizzando nella cache i profili utente, le liste di ruoli e i controlli dei permessi. L’SDK @auris/react usa TanStack Query internamente con uno staleTime predefinito che riduce le chiamate API ridondanti.
Usa operazioni batch quando disponibili. Usa endpoint bulk (es. operazioni bulk SCIM, scritture bulk tuple FGA) invece di chiamate API individuali per svolgere lo stesso lavoro con meno richieste.
Monitora gli header di rate limit in produzione. Registra il valore X-RateLimit-Remaining e imposta alert quando scende sotto una soglia. Questo ti dà un avviso anticipato prima che gli utenti inizino a vedere errori 429.
Guide Correlate
- Protezione dagli Attacchi — Lockout brute-force, CAPTCHA e la pipeline completa di sicurezza del login
- Gestione delle Sessioni — Durate dei token e comportamento del refresh
- Configurazione Webhook — Architettura event-driven per ridurre il polling