Class: JWT::PQ::JWKSet
- Inherits:
-
Object
- Object
- JWT::PQ::JWKSet
- Includes:
- Enumerable
- Defined in:
- lib/jwt/pq/jwk_set.rb,
lib/jwt/pq/jwks_loader.rb
Overview
A set of JWKs (RFC 7517 §5) for publication and kid-based lookup.
Typical producer flow — publish verification keys on
/.well-known/jwks.json:
Typical consumer flow — pick the right key for an incoming JWT by the
kid header:
The set indexes members by their RFC 7638 JWK Thumbprint, which is the
kid JWT::PQ::JWK#export emits. If you import a JWKS that uses custom (non-
thumbprint) kid values, lookup by those custom values is not
supported — rely on thumbprints when generating the set.
Defined Under Namespace
Classes: Loader
Class Method Summary collapse
-
.fetch(url) ⇒ JWKSet
Fetch a JWKS from a URL, honouring the process-global cache.
-
.import(source) ⇒ JWKSet
Import a JWKS from a Hash or JSON string.
Instance Method Summary collapse
-
#add(key) ⇒ JWKSet
Add a key to the set.
-
#each {|key| ... } ⇒ Enumerator
Iterate over the keys in insertion order.
-
#empty? ⇒ Boolean
True if the set is empty.
-
#export(include_private: false) ⇒ Hash{Symbol=>Array<Hash>}
Export the set as a JWKS hash.
-
#find(kid) ⇒ JWT::PQ::Key?
(also: #[])
Look up a key by its RFC 7638 thumbprint (the
kidfrom JWT::PQ::JWK#export). -
#initialize(keys = []) ⇒ JWKSet
constructor
Build a set from zero or more Keys.
-
#inspect ⇒ String
(also: #to_s)
Short diagnostic string — never contains key material.
-
#keys ⇒ Array<JWT::PQ::Key>
A frozen snapshot of the keys in the set.
-
#size ⇒ Integer
(also: #length)
Number of keys in the set.
-
#to_json ⇒ String
Serialize the set as a JWKS JSON document.
Constructor Details
Class Method Details
.fetch(url) ⇒ JWKSet
Fetch a JWKS from a URL, honouring the process-global cache.
Convenience wrapper around JWT::PQ::JWKSet::Loader#fetch using
JWT::PQ::JWKSet::Loader.default — the cache is shared across all callers, so
repeated hits on the same URL within cache_ttl seconds return
the in-memory set without touching the network.
See Loader for the full option reference (cache TTL, timeouts, body-size cap, HTTPS enforcement, ETag-based revalidation).
191 192 193 |
# File 'lib/jwt/pq/jwk_set.rb', line 191 def self.fetch(url, **) Loader.default.fetch(url, **) end |
.import(source) ⇒ JWKSet
Import a JWKS from a Hash or JSON string.
Each member is reconstructed via JWT::PQ::JWK.import; malformed members raise KeyError.
ML-DSA public keys are ~1.3–2.6 KB each, so a JWKS with N keys is
at least N × ~2 KB. When ingesting untrusted JWKS payloads (e.g.
a remote /.well-known/jwks.json), bound the HTTP body size
before calling import — this method does not cap the number of
members.
144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/jwt/pq/jwk_set.rb', line 144 def self.import(source) hash = coerce_to_hash(source) raise KeyError, "Expected Hash for JWKS body, got #{hash.class}" unless hash.is_a?(Hash) hash = hash.transform_keys(&:to_s) raise KeyError, "Missing 'keys' in JWKS" unless hash.key?("keys") raise KeyError, "'keys' must be an Array" unless hash["keys"].is_a?(Array) members = hash["keys"].map { |jwk| JWT::PQ::JWK.import(jwk) } new(members) end |
Instance Method Details
#add(key) ⇒ JWKSet
Add a key to the set.
Idempotent: if a key with the same RFC 7638 thumbprint is already
in the set, the call is a no-op (Set semantics). The thumbprint
is computed before any mutation, so a failure to derive the
kid leaves the set unchanged.
54 55 56 57 58 59 60 61 62 63 |
# File 'lib/jwt/pq/jwk_set.rb', line 54 def add(key) raise KeyError, "Expected a JWT::PQ::Key, got #{key.class}" unless key.is_a?(JWT::PQ::Key) kid = key.jwk_thumbprint return self if @kid_index.key?(kid) @keys << key @kid_index[kid] = key self end |
#each {|key| ... } ⇒ Enumerator
Iterate over the keys in insertion order.
69 70 71 |
# File 'lib/jwt/pq/jwk_set.rb', line 69 def each(&) @keys.each(&) end |
#empty? ⇒ Boolean
Returns true if the set is empty.
80 81 82 |
# File 'lib/jwt/pq/jwk_set.rb', line 80 def empty? @keys.empty? end |
#export(include_private: false) ⇒ Hash{Symbol=>Array<Hash>}
Export the set as a JWKS hash.
104 105 106 |
# File 'lib/jwt/pq/jwk_set.rb', line 104 def export(include_private: false) { keys: @keys.map { |k| JWK.new(k).export(include_private: include_private) } } end |
#find(kid) ⇒ JWT::PQ::Key? Also known as: []
Look up a key by its RFC 7638 thumbprint (the kid from JWT::PQ::JWK#export).
88 89 90 |
# File 'lib/jwt/pq/jwk_set.rb', line 88 def find(kid) @kid_index[kid] end |
#inspect ⇒ String Also known as: to_s
Returns short diagnostic string — never contains key material.
123 124 125 |
# File 'lib/jwt/pq/jwk_set.rb', line 123 def inspect "#<#{self.class} size=#{size}>" end |
#keys ⇒ Array<JWT::PQ::Key>
Returns a frozen snapshot of the keys in the set.
94 95 96 |
# File 'lib/jwt/pq/jwk_set.rb', line 94 def keys @keys.dup.freeze end |
#size ⇒ Integer Also known as: length
Returns number of keys in the set.
74 75 76 |
# File 'lib/jwt/pq/jwk_set.rb', line 74 def size @keys.size end |
#to_json ⇒ String
Serialize the set as a JWKS JSON document.
Always emits public-only keys — the priv field is never
written out. This keeps the method safe for arbitrary nesting
inside other JSON (e.g. { jwks: set }.to_json), where Ruby's
stdlib JSON passes a generator state as a positional argument.
To publish private material (unusual), call
JSON.generate(set.export(include_private: true)) explicitly.
118 119 120 |
# File 'lib/jwt/pq/jwk_set.rb', line 118 def to_json(*) export.to_json(*) end |