Skip to content

PATTERN Cited by 1 source

JWKS cache with thundering-herd protection

Intent

Cache the identity provider's JSON Web Key Set (JWKS) to avoid per-request network calls for JWT signature verification, while protecting the JWKS endpoint from a thundering herd when the cache expires or a key rotation triggers a cache miss.

Problem

Every JWT validation requires the signing key. Fetching the key from the identity provider's /.well-known/jwks.json endpoint on every request adds latency and creates a runtime dependency. Caching the key set eliminates this — but when the cache TTL expires, many concurrent requests simultaneously discover the cache is stale and all attempt to refresh. Under high concurrency, this thundering herd can overwhelm the JWKS endpoint or cause mass request failures during the refresh window.

Solution

A mutex-guarded refresh with a time-based re-fetch guard: if another goroutine/thread already refreshed within the last N seconds, the current goroutine uses the already-refreshed cache instead of making another fetch.

func (c *JWKSCache) refresh() error {
    c.mu.Lock()
    defer c.mu.Unlock()

    // If another goroutine already refreshed within guard window, skip
    if time.Since(c.fetched) < 30*time.Second {
        return nil
    }

    // Fetch from JWKS endpoint...
    c.fetched = time.Now()
    return nil
}

This is a simplified singleflight pattern: exactly one goroutine performs the expensive operation; all others observe the result.

Canonical parameters

Parameter Value Rationale
Cache TTL 1 hour Cognito rotates keys infrequently; hourly refresh balances freshness vs load
Re-fetch guard 30 seconds Prevents stampede; 30s is long enough that a JWKS fetch completes

(Source: sources/2026-06-29-aws-dual-token-authentication-for-nakama-game-servers)

Key properties

  • Correctness on rotation: if a JWT arrives signed with a kid not in the cache, the cache can be proactively refreshed (still protected by the guard). This handles key rotation without waiting for TTL expiry.
  • Bounded load: at most one JWKS fetch per 30-second window, regardless of request concurrency.
  • No external dependency after cache warm: once cached, JWT validation is CPU-only (RSA signature verification). The identity provider can be temporarily unreachable without affecting request processing.

Relationship to other patterns

Seen in

Last updated · 562 distilled / 1,660 read