Skip to content

Commit

Permalink
Added a way of allowing tokens to specify a nil kid
Browse files Browse the repository at this point in the history
  • Loading branch information
bellebaum authored and anakinj committed Jan 31, 2023
1 parent 001d9c4 commit 425c107
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 4 deletions.
2 changes: 1 addition & 1 deletion lib/jwt/decode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def verify_algo

def set_key
@key = find_key(&@keyfinder) if @keyfinder
@key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks]).key_for(header['kid']) if @options[:jwks]
@key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks], allow_nil_kid: @options[:allow_nil_kid]).key_for(header['kid']) if @options[:jwks]
if (x5c_options = @options[:x5c])
@key = X5cKeyFinder.new(x5c_options[:root_certificates], x5c_options[:crls]).from(header['x5c'])
end
Expand Down
9 changes: 6 additions & 3 deletions lib/jwt/jwk/key_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module JWT
module JWK
class KeyFinder
def initialize(options)
@allow_nil_kid = options[:allow_nil_kid]
jwks_or_loader = options[:jwks]

@jwks_loader = if jwks_or_loader.respond_to?(:call)
Expand All @@ -14,7 +15,7 @@ def initialize(options)
end

def key_for(kid)
raise ::JWT::DecodeError, 'No key id (kid) found from token headers' unless kid
raise ::JWT::DecodeError, 'No key id (kid) found from token headers' unless kid || @allow_nil_kid

jwk = resolve_key(kid)

Expand All @@ -27,15 +28,17 @@ def key_for(kid)
private

def resolve_key(kid)
key_matcher = ->(key) { (kid.nil? && @allow_nil_kid) || key[:kid] == kid }

# First try without invalidation to facilitate application caching
@jwks ||= JWT::JWK::Set.new(@jwks_loader.call(kid: kid))
jwk = @jwks.find { |key| key[:kid] == 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, kid: kid))
@jwks.find { |key| key[:kid] == kid }
@jwks.find { |key| key_matcher.call(key) }
end
end
end
Expand Down
11 changes: 11 additions & 0 deletions spec/jwk/decode_with_jwk_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@
end
end

context 'when the token kid is nil' do
let(:token_headers) { {} }
context 'and allow_nil_kid is specified' do
it 'decodes the token' do
key_loader = ->(_options) { JSON.parse(JSON.generate(public_jwks)) }
payload, _header = described_class.decode(signed_token, nil, true, { algorithms: ['RS512'], jwks: key_loader, allow_nil_kid: true })
expect(payload).to eq(token_payload)
end
end
end

context 'mixing algorithms using kid header' do
let(:hmac_jwk) { JWT::JWK.new('secret') }
let(:rsa_jwk) { JWT::JWK.new(OpenSSL::PKey::RSA.new(2048)) }
Expand Down

0 comments on commit 425c107

Please sign in to comment.