Class: RackJwtVerifier::KeySource::Remote

Inherits:
Object
  • Object
show all
Defined in:
lib/rack_jwt_verifier/key_source.rb

Overview

Shared machinery for key material fetched over HTTPS: caching, strict timeouts, a size cap, single-flight fetching within the process, and a rate-limited refresh for key rotation. Subclasses say how to parse the body and which cache namespace to use.

Direct Known Subclasses

RemoteJwks, RemotePem

Constant Summary collapse

DEFAULT_HTTP_TIMEOUT =

Timeout (seconds) applied separately to opening the connection and to reading the response. Kept short so a slow endpoint cannot pin every request thread on a cache miss.

5
MAX_RESPONSE_BYTES =

Largest response body (bytes) we are willing to read. A PEM key is well under 1 KB and a JWKS with a handful of keys a few KB.

64 * 1024
DEFAULT_CACHE_TTL =

How long (seconds) fetched material is served from the cache.

300
DEFAULT_REFETCH_INTERVAL =

Minimum gap (seconds) between two rotation-triggered refetches, so a flood of tokens with bad signatures or unknown kids cannot turn into a flood of requests to the provider.

60

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url:, cache:, cache_ttl: DEFAULT_CACHE_TTL, http_timeout: DEFAULT_HTTP_TIMEOUT, refetch_interval: DEFAULT_REFETCH_INTERVAL, allow_insecure_http: false, logger: nil) ⇒ Remote

Returns a new instance of Remote.



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/rack_jwt_verifier/key_source.rb', line 118

def initialize(url:, cache:, cache_ttl: DEFAULT_CACHE_TTL, http_timeout: DEFAULT_HTTP_TIMEOUT,
               refetch_interval: DEFAULT_REFETCH_INTERVAL, allow_insecure_http: false, logger: nil)
  @url = url.to_s
  @uri = parse_url(@url, allow_insecure_http: allow_insecure_http)
  @cache = cache
  @logger = logger || Logger.new(IO::NULL)
  @cache_ttl = cache_ttl
  @http_timeout = http_timeout
  @refetch_interval = refetch_interval
  # Scoped to the URL so two verifiers sharing one cache store (two SSO
  # providers behind one Redis) never read each other's key.
  @cache_key = "#{cache_key_prefix}:#{Digest::SHA256.hexdigest(@url)[0, 16]}"

  @fetch_lock = Mutex.new # single-flight: one network fetch per process on a cold cache
  @state_lock = Mutex.new # guards @parsed and @last_refetch_at
  @parsed = nil           # [body, parsed material] of the last body parsed
  @last_refetch_at = nil  # monotonic clock
end

Instance Attribute Details

#cache_keyObject (readonly)

Returns the value of attribute cache_key.



116
117
118
# File 'lib/rack_jwt_verifier/key_source.rb', line 116

def cache_key
  @cache_key
end

#urlObject (readonly)

Returns the value of attribute url.



116
117
118
# File 'lib/rack_jwt_verifier/key_source.rb', line 116

def url
  @url
end

Instance Method Details

#refresh!Object

Refetches the material unless a refresh happened less than refetch_interval seconds ago. Returns true if it actually refetched.



139
140
141
142
143
144
145
146
147
148
# File 'lib/rack_jwt_verifier/key_source.rb', line 139

def refresh!
  @state_lock.synchronize do
    now = monotonic_now
    return false if @last_refetch_at && now - @last_refetch_at < @refetch_interval

    @last_refetch_at = now
  end
  fetch_and_store
  true
end