Přeskočit na hlavní obsah

Rate Limits

Klubero SSO implementuje rate limiting pro zajištění stability služby a prevenci zneužití.

Tabulka Rate Limits​

Všechny rate limity jsou aplikovány per IP adresa.

EndpointLimitOknoÚčel
/connect/token10 požadavků15 minutOchrana proti brute-force útokům na přihlašovací údaje
/api/twofactor/*5 požadavků5 minutOchrana proti brute-force útokům na ověřovací kódy
/api/magiclink/send3 požadavky5 minutPrevence spamu e-mailů
/connect/authorize30 požadavků1 minutaOchrana proti DoS a enumeraci redirect URI

Odpověď při překročení limitu​

Při překročení rate limitu vrátí server HTTP status 429 Too Many Requests s hlavičkou Retry-After (počet sekund do dalšího povoleného požadavku) a krátkým tělem v prostém textu:

HTTP/1.1 429 Too Many Requests
Retry-After: 300
Content-Type: text/plain

API calls quota exceeded! maximum admitted 10 per 15m.
note

Odpověď není ve formátu JSON. Pro určení doby čekání vždy čtěte hodnotu hlavičky Retry-After (v sekundách); nespoléhejte na parsování těla odpovědi. Přesné znění textového těla se může lišit podle konfigurace.

Zpracování Rate Limits​

  1. Kontrolujte status odpovědi: Hledejte HTTP 429
  2. Čtěte Retry-After header: Udává počet sekund k čekání
  3. Implementujte exponential backoff: Pro automatizované systémy
  4. Cachujte tokeny: Opakovaně používejte access tokeny dokud nevyprší

Osvědčené postupy​

Dělejte​

  • Cachujte a opakovaně používejte tokeny dokud nevyprší
  • Používejte refresh tokeny místo opětovné autentizace
  • Implementujte správné zpracování chyb s backoff
  • Seskupujte operace kde je to možné

Nedělejte​

  • Žádat o nový token pro každé API volání
  • Znovu se autentizovat pro každou relaci
  • Okamžitě opakovat při selhání
  • Dělat mnoho sekvenčních požadavků

Příklad: Cachování tokenů​

let tokenCache = {
accessToken: null,
expiresAt: null
};

async function getAccessToken() {
// Vrátit cachovaný token pokud je stále platný (s 60s rezervou)
if (tokenCache.accessToken && tokenCache.expiresAt > Date.now() + 60000) {
return tokenCache.accessToken;
}

// Požádat o nový token
const response = await fetch('/connect/token', {
method: 'POST',
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET
})
});

if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 60;
throw new Error(`Rate limited. Zkuste znovu za ${retryAfter} sekund.`);
}

const data = await response.json();
tokenCache.accessToken = data.access_token;
tokenCache.expiresAt = Date.now() + (data.expires_in * 1000);

return tokenCache.accessToken;
}