Skip to content

Commit

Permalink
Simplify the JWT::Verify class
Browse files Browse the repository at this point in the history
  • Loading branch information
anakinj committed Jul 22, 2024
1 parent a17048a commit 236bc49
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 376 deletions.
1 change: 0 additions & 1 deletion lib/jwt/decode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ def find_key(&keyfinder)

def verify_claims
Verify.verify_claims(payload, @options)
Verify.verify_required_claims(payload, @options)
end

def validate_segment_count!
Expand Down
67 changes: 28 additions & 39 deletions lib/jwt/verify.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@ class Verify
leeway: 0
}.freeze

class << self
%w[verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub verify_required_claims].each do |method_name|
define_method method_name do |payload, options|
new(payload, options).send(method_name)
end
end
ClaimsContext = Struct.new(:payload, keyword_init: true)

class << self
def verify_claims(payload, options)
options.each do |key, val|
next unless key.to_s =~ /verify/

Verify.send(key, payload, options) if val
new(payload, options).tap do |verifier|
verifier.verify_aud
verifier.verify_expiration
verifier.verify_iat
verifier.verify_iss
verifier.verify_jti
verifier.verify_not_before
verifier.verify_sub
verifier.verify_required_claims
end
end
end
Expand All @@ -31,69 +32,57 @@ def initialize(payload, options)
end

def verify_aud
return unless @options[:verify_aud]
return unless (options_aud = @options[:aud])

Claims::Audience.new(expected_audience: options_aud).validate!(context: { payload: @payload })
Claims::Audience.new(expected_audience: options_aud).validate!(context: ClaimsContext.new(payload: @payload))
end

def verify_expiration
return unless @options[:verify_expiration]
return unless contains_key?(@payload, 'exp')

Claims::Expiration.new(leeway: exp_leeway).validate!(context: { payload: @payload })
Claims::Expiration.new(leeway: exp_leeway).validate!(context: ClaimsContext.new(payload: @payload))
end

def verify_iat
return unless @options[:verify_iat]
return unless contains_key?(@payload, 'iat')

iat = @payload['iat']
raise JWT::InvalidIatError, 'Invalid iat' if !iat.is_a?(Numeric) || iat.to_f > Time.now.to_f
Claims::IssuedAt.new.validate!(context: ClaimsContext.new(payload: @payload))
end

def verify_iss
return unless @options[:verify_iss]
return unless (options_iss = @options[:iss])

iss = @payload['iss']

options_iss = Array(options_iss).map { |item| item.is_a?(Symbol) ? item.to_s : item }

case iss
when *options_iss
nil
else
raise JWT::InvalidIssuerError, "Invalid issuer. Expected #{options_iss}, received #{iss || '<none>'}"
end
Claims::Issuer.new(issuers: options_iss).validate!(context: ClaimsContext.new(payload: @payload))
end

def verify_jti
options_verify_jti = @options[:verify_jti]
jti = @payload['jti']

if options_verify_jti.respond_to?(:call)
verified = options_verify_jti.arity == 2 ? options_verify_jti.call(jti, @payload) : options_verify_jti.call(jti)
raise JWT::InvalidJtiError, 'Invalid jti' unless verified
elsif jti.to_s.strip.empty?
raise JWT::InvalidJtiError, 'Missing jti'
end
return unless @options[:verify_jti]

Claims::JwtId.new(validator: @options[:verify_jti]).validate!(context: ClaimsContext.new(payload: @payload))
end

def verify_not_before
return unless @options[:verify_not_before]
return unless contains_key?(@payload, 'nbf')
raise JWT::ImmatureSignature, 'Signature nbf has not been reached' if @payload['nbf'].to_i > (Time.now.to_i + nbf_leeway)

Claims::NotBefore.new(leeway: nbf_leeway).validate!(context: ClaimsContext.new(payload: @payload))
end

def verify_sub
return unless @options[:verify_sub]
return unless (options_sub = @options[:sub])

sub = @payload['sub']
raise JWT::InvalidSubError, "Invalid subject. Expected #{options_sub}, received #{sub || '<none>'}" unless sub.to_s == options_sub.to_s
Claims::Subject.new(expected_subject: options_sub).validate!(context: ClaimsContext.new(payload: @payload))
end

def verify_required_claims
return unless (options_required_claims = @options[:required_claims])

options_required_claims.each do |required_claim|
raise JWT::MissingRequiredClaim, "Missing required claim #{required_claim}" unless contains_key?(@payload, required_claim)
end
Claims::Required.new(required_claims: options_required_claims).validate!(context: ClaimsContext.new(payload: @payload))
end

private
Expand Down
Loading

0 comments on commit 236bc49

Please sign in to comment.