Skip to content

Commit 08d3bdd

Browse files
committed
Utilize key_fields instead of manually defining key matching order
1 parent 0212e3d commit 08d3bdd

File tree

3 files changed

+32
-16
lines changed

3 files changed

+32
-16
lines changed

lib/jwt/decode.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,13 @@ def verify_algo
6060

6161
def set_key
6262
@key = find_key(&@keyfinder) if @keyfinder
63-
@key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks], allow_nil_kid: @options[:allow_nil_kid]).call(token) if @options[:jwks]
63+
if @options[:jwks]
64+
@key = ::JWT::JWK::KeyFinder.new(
65+
jwks: @options[:jwks],
66+
allow_nil_kid: @options[:allow_nil_kid],
67+
key_fields: @options[:key_fields]
68+
).call(token)
69+
end
6470

6571
return unless (x5c_options = @options[:x5c])
6672

lib/jwt/jwk/key_finder.rb

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ class KeyFinder
99
# @param [Hash] options the options to create a KeyFinder with
1010
# @option options [Proc, JWT::JWK::Set] :jwks the jwks or a loader proc
1111
# @option options [Boolean] :allow_nil_kid whether to allow nil kid
12+
# @option options [Array] :key_fields the fields to use for key matching,
13+
# the order of the fields are used to determine
14+
# the priority of the keys.
1215
def initialize(options)
1316
@allow_nil_kid = options[:allow_nil_kid]
1417
jwks_or_loader = options[:jwks]
@@ -18,6 +21,8 @@ def initialize(options)
1821
else
1922
->(_options) { jwks_or_loader }
2023
end
24+
25+
@key_fields = options[:key_fields] || %i[kid]
2126
end
2227

2328
# Returns the verification key for the given kid
@@ -36,21 +41,16 @@ def key_for(kid, key_field = :kid)
3641
# Returns the key for the given token
3742
# @param [JWT::EncodedToken] token the token
3843
def call(token)
39-
kid = token.header['kid']
40-
x5t = token.header['x5t']
41-
x5c = token.header['x5c']
42-
43-
if kid
44-
key_for(kid, :kid)
45-
elsif x5t
46-
key_for(x5t, :x5t)
47-
elsif x5c
48-
key_for(x5c, :x5c)
49-
elsif @allow_nil_kid
50-
key_for(kid)
51-
else
52-
raise ::JWT::DecodeError, 'No key id (kid) or x5t found from token headers'
44+
@key_fields.each do |key_field|
45+
field_value = token.header[key_field.to_s]
46+
47+
return key_for(field_value, key_field) if field_value
5348
end
49+
50+
raise ::JWT::DecodeError, 'No key id (kid) or x5t found from token headers' unless @allow_nil_kid
51+
52+
kid = token.header['kid']
53+
key_for(kid)
5454
end
5555

5656
private

spec/jwt/jwk/decode_with_jwk_spec.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,17 @@
4444
let(:valid_key) { jwk.export.merge({ x5t: x5t }) }
4545
let(:token_headers) { { x5t: x5t } }
4646
it 'is able to decode the token' do
47-
payload, _header = described_class.decode(signed_token, nil, true, { algorithms: [algorithm], jwks: public_jwks })
47+
payload, _header = described_class.decode(signed_token, nil, true, { algorithms: [algorithm], jwks: public_jwks, key_fields: [:x5t] })
48+
expect(payload).to eq(token_payload)
49+
end
50+
end
51+
52+
context 'and both kid and x5t is in the set' do
53+
let(:x5t) { Base64.urlsafe_encode64(OpenSSL::Digest::SHA1.new(keypair.to_der).digest, padding: false) }
54+
let(:valid_key) { jwk.export.merge({ x5t: x5t }) }
55+
let(:token_headers) { { x5t: x5t, kid: 'NOT_A_MATCH' } }
56+
it 'is able to decode the token based on the priority of the key defined in key_fields' do
57+
payload, _header = described_class.decode(signed_token, nil, true, { algorithms: [algorithm], jwks: public_jwks, key_fields: %i[x5t kid] })
4858
expect(payload).to eq(token_payload)
4959
end
5060
end

0 commit comments

Comments
 (0)