Class: Clowk::Jwks

Inherits:
Object
  • Object
show all
Defined in:
lib/clowk/jwks.rb

Overview

Fetches and caches Clowk's public key set.

Keys are cached process-wide because verifying a token must not cost an HTTP round trip. A kid the cache has never seen forces one refetch — that is what makes key rotation invisible to consumers instead of an outage.

Constant Summary collapse

CACHE_TTL =
600
WELL_KNOWN_PATH =
"/.well-known/jwks.json"
REFETCH_COOLDOWN =

Rotation should resolve in one refetch. Anything beyond that is a misconfigured issuer or a forged kid, and hammering the auth server on every unverifiable token turns a bad token into an outage.

10

Class Method Summary collapse

Class Method Details

.clear_cache!Object



26
27
28
29
30
31
# File 'lib/clowk/jwks.rb', line 26

def clear_cache!
  @cache_mutex.synchronize do
    @cache = {}
    @last_refetch_at = {}
  end
end

.default_urlObject



50
51
52
53
54
55
56
# File 'lib/clowk/jwks.rb', line 50

def default_url
  base = Clowk.config.subdomain_url || Clowk::Subdomain.resolve_url!

  "#{base.to_s.chomp("/")}#{WELL_KNOWN_PATH}"
rescue ConfigurationError
  raise ConfigurationError, "set jwks_url, subdomain_url or publishable_key to verify RS256 tokens"
end

.key_for(kid, jwks_url: Clowk.config.jwks_url) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/clowk/jwks.rb', line 33

def key_for(kid, jwks_url: Clowk.config.jwks_url)
  url = jwks_url || default_url

  key = lookup(url, kid)
  return key if key

  # Cold or expired cache: this is the ordinary first fetch, not a
  # rotation, so it must not spend the miss budget. Data is fresh
  # afterwards, so a miss here means the kid is genuinely unknown.
  return fetch_and_lookup(url, kid) unless warm?(url)

  # Warm cache and an unknown kid: the set may have rotated under us.
  return nil unless claim_refetch_slot!(url)

  fetch_and_lookup(url, kid)
end