forked from jwt/ruby-jwt
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkey_finder.rb
More file actions
73 lines (57 loc) · 2.56 KB
/
Copy pathkey_finder.rb
File metadata and controls
73 lines (57 loc) · 2.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# frozen_string_literal: true
module JWT
module JWK
# JSON Web Key keyfinder
# To find the key for a given kid
class KeyFinder
# Initializes a new KeyFinder instance.
# @param [Hash] options the options to create a KeyFinder with
# @option options [Proc, JWT::JWK::Set] :jwks the jwks or a loader proc
# @option options [Boolean] :allow_nil_kid whether to allow nil kid
# @option options [Array] :key_fields the fields to use for key matching,
# the order of the fields are used to determine
# the priority of the keys.
def initialize(options)
@allow_nil_kid = options[:allow_nil_kid]
jwks_or_loader = options[:jwks]
@jwks_loader = if jwks_or_loader.respond_to?(:call)
jwks_or_loader
else
->(_options) { jwks_or_loader }
end
@key_fields = options[:key_fields] || %i[kid]
end
# Returns the verification key for the given kid
# @param [String] kid the key id
def key_for(kid, key_field = :kid)
raise ::JWT::MalformedTokenError, "Invalid type for #{key_field} header parameter" unless kid.nil? || kid.is_a?(String)
jwk = resolve_key(kid, key_field)
raise ::JWT::SignatureError, 'No keys found in jwks' unless @jwks.any?
raise ::JWT::SignatureError, "Could not find public key for kid #{kid}" unless jwk
jwk.verify_key
end
# Returns the key for the given token
# @param [JWT::EncodedToken] token the token
def call(token)
@key_fields.each do |key_field|
field_value = token.header[key_field.to_s]
return key_for(field_value, key_field) if field_value
end
raise ::JWT::SignatureError, 'No key id (kid) or x5t found from token headers' unless @allow_nil_kid
kid = token.header['kid']
key_for(kid)
end
private
def resolve_key(kid, key_field)
key_matcher = ->(key) { (kid.nil? && @allow_nil_kid) || key[key_field] == kid }
# First try without invalidation to facilitate application caching
@jwks ||= JWT::JWK::Set.new(@jwks_loader.call(key_field => kid))
jwk = @jwks.find { |key| key_matcher.call(key) }
return jwk if jwk
# Second try, invalidate for backwards compatibility
@jwks = JWT::JWK::Set.new(@jwks_loader.call(invalidate: true, kid_not_found: true, key_field => kid))
@jwks.find { |key| key_matcher.call(key) }
end
end
end
end