jwt 2.9.1 → 2.10.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -0
- data/README.md +170 -82
- data/lib/jwt/base64.rb +3 -0
- data/lib/jwt/claims/audience.rb +10 -0
- data/lib/jwt/claims/crit.rb +35 -0
- data/lib/jwt/claims/decode_verifier.rb +40 -0
- data/lib/jwt/claims/expiration.rb +10 -0
- data/lib/jwt/claims/issued_at.rb +7 -0
- data/lib/jwt/claims/issuer.rb +10 -0
- data/lib/jwt/claims/jwt_id.rb +10 -0
- data/lib/jwt/claims/not_before.rb +10 -0
- data/lib/jwt/claims/numeric.rb +47 -13
- data/lib/jwt/claims/required.rb +10 -0
- data/lib/jwt/claims/subject.rb +10 -0
- data/lib/jwt/claims/verification_methods.rb +20 -0
- data/lib/jwt/claims/verifier.rb +61 -0
- data/lib/jwt/claims.rb +52 -16
- data/lib/jwt/claims_validator.rb +18 -0
- data/lib/jwt/configuration/container.rb +20 -0
- data/lib/jwt/configuration/decode_configuration.rb +24 -0
- data/lib/jwt/configuration/jwk_configuration.rb +1 -0
- data/lib/jwt/configuration.rb +8 -0
- data/lib/jwt/decode.rb +28 -67
- data/lib/jwt/deprecations.rb +1 -0
- data/lib/jwt/encode.rb +17 -56
- data/lib/jwt/encoded_token.rb +139 -0
- data/lib/jwt/error.rb +34 -0
- data/lib/jwt/json.rb +1 -1
- data/lib/jwt/jwa/compat.rb +32 -0
- data/lib/jwt/jwa/ecdsa.rb +7 -6
- data/lib/jwt/jwa/eddsa.rb +7 -6
- data/lib/jwt/jwa/hmac.rb +6 -3
- data/lib/jwt/jwa/hmac_rbnacl.rb +5 -0
- data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +5 -0
- data/lib/jwt/jwa/none.rb +1 -0
- data/lib/jwt/jwa/ps.rb +2 -3
- data/lib/jwt/jwa/rsa.rb +2 -3
- data/lib/jwt/jwa/signing_algorithm.rb +4 -0
- data/lib/jwt/jwa/unsupported.rb +1 -0
- data/lib/jwt/jwa/wrapper.rb +1 -0
- data/lib/jwt/jwa.rb +17 -4
- data/lib/jwt/jwk/ec.rb +2 -3
- data/lib/jwt/jwk/hmac.rb +2 -3
- data/lib/jwt/jwk/key_base.rb +1 -0
- data/lib/jwt/jwk/key_finder.rb +1 -0
- data/lib/jwt/jwk/kid_as_key_digest.rb +1 -0
- data/lib/jwt/jwk/okp_rbnacl.rb +3 -4
- data/lib/jwt/jwk/rsa.rb +2 -3
- data/lib/jwt/jwk/set.rb +2 -0
- data/lib/jwt/jwk.rb +1 -0
- data/lib/jwt/token.rb +112 -0
- data/lib/jwt/verify.rb +40 -0
- data/lib/jwt/version.rb +30 -9
- data/lib/jwt.rb +19 -0
- metadata +13 -7
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module Claims
|
5
|
+
# Responsible of validation the crit header
|
6
|
+
class Crit
|
7
|
+
# Initializes a new Crit instance.
|
8
|
+
#
|
9
|
+
# @param expected_crits [String] the expected crit header values for the JWT token.
|
10
|
+
def initialize(expected_crits:)
|
11
|
+
@expected_crits = Array(expected_crits)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Verifies the critical claim ('crit') in the JWT token header.
|
15
|
+
#
|
16
|
+
# @param context [Object] the context containing the JWT payload and header.
|
17
|
+
# @param _args [Hash] additional arguments (not used).
|
18
|
+
# @raise [JWT::InvalidCritError] if the crit claim is invalid.
|
19
|
+
# @return [nil]
|
20
|
+
def verify!(context:, **_args)
|
21
|
+
raise(JWT::InvalidCritError, 'Crit header missing') unless context.header['crit']
|
22
|
+
raise(JWT::InvalidCritError, 'Crit header should be an array') unless context.header['crit'].is_a?(Array)
|
23
|
+
|
24
|
+
missing = (expected_crits - context.header['crit'])
|
25
|
+
raise(JWT::InvalidCritError, "Crit header missing expected values: #{missing.join(', ')}") if missing.any?
|
26
|
+
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :expected_crits
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module Claims
|
5
|
+
# Context class to contain the data passed to individual claim validators
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
VerificationContext = Struct.new(:payload, keyword_init: true)
|
9
|
+
|
10
|
+
# Verifiers to support the ::JWT.decode method
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
module DecodeVerifier
|
14
|
+
VERIFIERS = {
|
15
|
+
verify_expiration: ->(options) { Claims::Expiration.new(leeway: options[:exp_leeway] || options[:leeway]) },
|
16
|
+
verify_not_before: ->(options) { Claims::NotBefore.new(leeway: options[:nbf_leeway] || options[:leeway]) },
|
17
|
+
verify_iss: ->(options) { options[:iss] && Claims::Issuer.new(issuers: options[:iss]) },
|
18
|
+
verify_iat: ->(*) { Claims::IssuedAt.new },
|
19
|
+
verify_jti: ->(options) { Claims::JwtId.new(validator: options[:verify_jti]) },
|
20
|
+
verify_aud: ->(options) { options[:aud] && Claims::Audience.new(expected_audience: options[:aud]) },
|
21
|
+
verify_sub: ->(options) { options[:sub] && Claims::Subject.new(expected_subject: options[:sub]) },
|
22
|
+
required_claims: ->(options) { Claims::Required.new(required_claims: options[:required_claims]) }
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
private_constant(:VERIFIERS)
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# @api private
|
29
|
+
def verify!(payload, options)
|
30
|
+
VERIFIERS.each do |key, verifier_builder|
|
31
|
+
next unless options[key] || options[key.to_s]
|
32
|
+
|
33
|
+
verifier_builder&.call(options)&.verify!(context: VerificationContext.new(payload: payload))
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -2,11 +2,21 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module Claims
|
5
|
+
# The Expiration class is responsible for validating the expiration claim ('exp') in a JWT token.
|
5
6
|
class Expiration
|
7
|
+
# Initializes a new Expiration instance.
|
8
|
+
#
|
9
|
+
# @param leeway [Integer] the amount of leeway (in seconds) to allow when validating the expiration time. Default: 0.
|
6
10
|
def initialize(leeway:)
|
7
11
|
@leeway = leeway || 0
|
8
12
|
end
|
9
13
|
|
14
|
+
# Verifies the expiration claim ('exp') in the JWT token.
|
15
|
+
#
|
16
|
+
# @param context [Object] the context containing the JWT payload.
|
17
|
+
# @param _args [Hash] additional arguments (not used).
|
18
|
+
# @raise [JWT::ExpiredSignature] if the token has expired.
|
19
|
+
# @return [nil]
|
10
20
|
def verify!(context:, **_args)
|
11
21
|
return unless context.payload.is_a?(Hash)
|
12
22
|
return unless context.payload.key?('exp')
|
data/lib/jwt/claims/issued_at.rb
CHANGED
@@ -2,7 +2,14 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module Claims
|
5
|
+
# The IssuedAt class is responsible for validating the issued at claim ('iat') in a JWT token.
|
5
6
|
class IssuedAt
|
7
|
+
# Verifies the issued at claim ('iat') in the JWT token.
|
8
|
+
#
|
9
|
+
# @param context [Object] the context containing the JWT payload.
|
10
|
+
# @param _args [Hash] additional arguments (not used).
|
11
|
+
# @raise [JWT::InvalidIatError] if the issued at claim is invalid.
|
12
|
+
# @return [nil]
|
6
13
|
def verify!(context:, **_args)
|
7
14
|
return unless context.payload.is_a?(Hash)
|
8
15
|
return unless context.payload.key?('iat')
|
data/lib/jwt/claims/issuer.rb
CHANGED
@@ -2,11 +2,21 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module Claims
|
5
|
+
# The Issuer class is responsible for validating the issuer claim ('iss') in a JWT token.
|
5
6
|
class Issuer
|
7
|
+
# Initializes a new Issuer instance.
|
8
|
+
#
|
9
|
+
# @param issuers [String, Symbol, Array<String, Symbol>] the expected issuer(s) for the JWT token.
|
6
10
|
def initialize(issuers:)
|
7
11
|
@issuers = Array(issuers).map { |item| item.is_a?(Symbol) ? item.to_s : item }
|
8
12
|
end
|
9
13
|
|
14
|
+
# Verifies the issuer claim ('iss') in the JWT token.
|
15
|
+
#
|
16
|
+
# @param context [Object] the context containing the JWT payload.
|
17
|
+
# @param _args [Hash] additional arguments (not used).
|
18
|
+
# @raise [JWT::InvalidIssuerError] if the issuer claim is invalid.
|
19
|
+
# @return [nil]
|
10
20
|
def verify!(context:, **_args)
|
11
21
|
case (iss = context.payload['iss'])
|
12
22
|
when *issuers
|
data/lib/jwt/claims/jwt_id.rb
CHANGED
@@ -2,11 +2,21 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module Claims
|
5
|
+
# The JwtId class is responsible for validating the JWT ID claim ('jti') in a JWT token.
|
5
6
|
class JwtId
|
7
|
+
# Initializes a new JwtId instance.
|
8
|
+
#
|
9
|
+
# @param validator [#call] an object responding to `call` to validate the JWT ID.
|
6
10
|
def initialize(validator:)
|
7
11
|
@validator = validator
|
8
12
|
end
|
9
13
|
|
14
|
+
# Verifies the JWT ID claim ('jti') in the JWT token.
|
15
|
+
#
|
16
|
+
# @param context [Object] the context containing the JWT payload.
|
17
|
+
# @param _args [Hash] additional arguments (not used).
|
18
|
+
# @raise [JWT::InvalidJtiError] if the JWT ID claim is invalid or missing.
|
19
|
+
# @return [nil]
|
10
20
|
def verify!(context:, **_args)
|
11
21
|
jti = context.payload['jti']
|
12
22
|
if validator.respond_to?(:call)
|
@@ -2,11 +2,21 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module Claims
|
5
|
+
# The NotBefore class is responsible for validating the 'nbf' (Not Before) claim in a JWT token.
|
5
6
|
class NotBefore
|
7
|
+
# Initializes a new NotBefore instance.
|
8
|
+
#
|
9
|
+
# @param leeway [Integer] the amount of leeway (in seconds) to allow when validating the 'nbf' claim. Defaults to 0.
|
6
10
|
def initialize(leeway:)
|
7
11
|
@leeway = leeway || 0
|
8
12
|
end
|
9
13
|
|
14
|
+
# Verifies the 'nbf' (Not Before) claim in the JWT token.
|
15
|
+
#
|
16
|
+
# @param context [Object] the context containing the JWT payload.
|
17
|
+
# @param _args [Hash] additional arguments (not used).
|
18
|
+
# @raise [JWT::ImmatureSignature] if the 'nbf' claim has not been reached.
|
19
|
+
# @return [nil]
|
10
20
|
def verify!(context:, **_args)
|
11
21
|
return unless context.payload.is_a?(Hash)
|
12
22
|
return unless context.payload.key?('nbf')
|
data/lib/jwt/claims/numeric.rb
CHANGED
@@ -2,41 +2,75 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module Claims
|
5
|
+
# The Numeric class is responsible for validating numeric claims in a JWT token.
|
6
|
+
# The numeric claims are: exp, iat and nbf
|
5
7
|
class Numeric
|
6
|
-
|
7
|
-
|
8
|
+
# The Compat class provides backward compatibility for numeric claim validation.
|
9
|
+
# @api private
|
10
|
+
class Compat
|
11
|
+
def initialize(payload)
|
12
|
+
@payload = payload
|
13
|
+
end
|
8
14
|
|
9
|
-
|
15
|
+
def verify!
|
16
|
+
JWT::Claims.verify_payload!(@payload, :numeric)
|
17
|
+
end
|
10
18
|
end
|
11
19
|
|
20
|
+
# List of numeric claims that can be validated.
|
12
21
|
NUMERIC_CLAIMS = %i[
|
13
22
|
exp
|
14
23
|
iat
|
15
24
|
nbf
|
16
25
|
].freeze
|
17
26
|
|
18
|
-
|
19
|
-
|
27
|
+
private_constant(:NUMERIC_CLAIMS)
|
28
|
+
|
29
|
+
# @api private
|
30
|
+
def self.new(*args)
|
31
|
+
return super if args.empty?
|
32
|
+
|
33
|
+
Deprecations.warning('Calling ::JWT::Claims::Numeric.new with the payload will be removed in the next major version of ruby-jwt')
|
34
|
+
Compat.new(*args)
|
20
35
|
end
|
21
36
|
|
22
|
-
|
23
|
-
|
37
|
+
# Verifies the numeric claims in the JWT context.
|
38
|
+
#
|
39
|
+
# @param context [Object] the context containing the JWT payload.
|
40
|
+
# @raise [JWT::InvalidClaimError] if any numeric claim is invalid.
|
41
|
+
# @return [nil]
|
42
|
+
def verify!(context:)
|
43
|
+
validate_numeric_claims(context.payload)
|
44
|
+
end
|
24
45
|
|
25
|
-
|
46
|
+
# Verifies the numeric claims in the JWT payload.
|
47
|
+
#
|
48
|
+
# @param payload [Hash] the JWT payload containing the claims.
|
49
|
+
# @param _args [Hash] additional arguments (not used).
|
50
|
+
# @raise [JWT::InvalidClaimError] if any numeric claim is invalid.
|
51
|
+
# @return [nil]
|
52
|
+
# @deprecated The ::JWT::Claims::Numeric.verify! method will be removed in the next major version of ruby-jwt
|
53
|
+
def self.verify!(payload:, **_args)
|
54
|
+
Deprecations.warning('The ::JWT::Claims::Numeric.verify! method will be removed in the next major version of ruby-jwt.')
|
55
|
+
JWT::Claims.verify_payload!(payload, :numeric)
|
26
56
|
end
|
27
57
|
|
28
58
|
private
|
29
59
|
|
30
|
-
def validate_numeric_claims
|
60
|
+
def validate_numeric_claims(payload)
|
31
61
|
NUMERIC_CLAIMS.each do |claim|
|
32
|
-
validate_is_numeric(
|
62
|
+
validate_is_numeric(payload, claim)
|
33
63
|
end
|
34
64
|
end
|
35
65
|
|
36
|
-
def validate_is_numeric(claim)
|
37
|
-
return
|
66
|
+
def validate_is_numeric(payload, claim)
|
67
|
+
return unless payload.is_a?(Hash)
|
68
|
+
return unless payload.key?(claim) ||
|
69
|
+
payload.key?(claim.to_s)
|
70
|
+
|
71
|
+
return if payload[claim].is_a?(::Numeric) || payload[claim.to_s].is_a?(::Numeric)
|
38
72
|
|
39
|
-
raise InvalidPayload, "#{claim} claim must be a Numeric value but it is a #{
|
73
|
+
raise InvalidPayload, "#{claim} claim must be a Numeric value but it is a #{(payload[claim] || payload[claim.to_s]).class}"
|
40
74
|
end
|
41
75
|
end
|
42
76
|
end
|
data/lib/jwt/claims/required.rb
CHANGED
@@ -2,11 +2,21 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module Claims
|
5
|
+
# The Required class is responsible for validating that all required claims are present in a JWT token.
|
5
6
|
class Required
|
7
|
+
# Initializes a new Required instance.
|
8
|
+
#
|
9
|
+
# @param required_claims [Array<String>] the list of required claims.
|
6
10
|
def initialize(required_claims:)
|
7
11
|
@required_claims = required_claims
|
8
12
|
end
|
9
13
|
|
14
|
+
# Verifies that all required claims are present in the JWT payload.
|
15
|
+
#
|
16
|
+
# @param context [Object] the context containing the JWT payload.
|
17
|
+
# @param _args [Hash] additional arguments (not used).
|
18
|
+
# @raise [JWT::MissingRequiredClaim] if any required claim is missing.
|
19
|
+
# @return [nil]
|
10
20
|
def verify!(context:, **_args)
|
11
21
|
required_claims.each do |required_claim|
|
12
22
|
next if context.payload.is_a?(Hash) && context.payload.key?(required_claim)
|
data/lib/jwt/claims/subject.rb
CHANGED
@@ -2,11 +2,21 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module Claims
|
5
|
+
# The Subject class is responsible for validating the subject claim ('sub') in a JWT token.
|
5
6
|
class Subject
|
7
|
+
# Initializes a new Subject instance.
|
8
|
+
#
|
9
|
+
# @param expected_subject [String] the expected subject for the JWT token.
|
6
10
|
def initialize(expected_subject:)
|
7
11
|
@expected_subject = expected_subject.to_s
|
8
12
|
end
|
9
13
|
|
14
|
+
# Verifies the subject claim ('sub') in the JWT token.
|
15
|
+
#
|
16
|
+
# @param context [Object] the context containing the JWT payload.
|
17
|
+
# @param _args [Hash] additional arguments (not used).
|
18
|
+
# @raise [JWT::InvalidSubError] if the subject claim is invalid.
|
19
|
+
# @return [nil]
|
10
20
|
def verify!(context:, **_args)
|
11
21
|
sub = context.payload['sub']
|
12
22
|
raise(JWT::InvalidSubError, "Invalid subject. Expected #{expected_subject}, received #{sub || '<none>'}") unless sub.to_s == expected_subject
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module Claims
|
5
|
+
# @api private
|
6
|
+
module VerificationMethods
|
7
|
+
def verify_claims!(*options)
|
8
|
+
Verifier.verify!(self, *options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def claim_errors(*options)
|
12
|
+
Verifier.errors(self, *options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid_claims?(*options)
|
16
|
+
claim_errors(*options).empty?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module Claims
|
5
|
+
# @api private
|
6
|
+
module Verifier
|
7
|
+
VERIFIERS = {
|
8
|
+
exp: ->(options) { Claims::Expiration.new(leeway: options.dig(:exp, :leeway)) },
|
9
|
+
nbf: ->(options) { Claims::NotBefore.new(leeway: options.dig(:nbf, :leeway)) },
|
10
|
+
iss: ->(options) { Claims::Issuer.new(issuers: options[:iss]) },
|
11
|
+
iat: ->(*) { Claims::IssuedAt.new },
|
12
|
+
jti: ->(options) { Claims::JwtId.new(validator: options[:jti]) },
|
13
|
+
aud: ->(options) { Claims::Audience.new(expected_audience: options[:aud]) },
|
14
|
+
sub: ->(options) { Claims::Subject.new(expected_subject: options[:sub]) },
|
15
|
+
crit: ->(options) { Claims::Crit.new(expected_crits: options[:crit]) },
|
16
|
+
required: ->(options) { Claims::Required.new(required_claims: options[:required]) },
|
17
|
+
numeric: ->(*) { Claims::Numeric.new }
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
private_constant(:VERIFIERS)
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# @api private
|
24
|
+
def verify!(context, *options)
|
25
|
+
iterate_verifiers(*options) do |verifier, verifier_options|
|
26
|
+
verify_one!(context, verifier, verifier_options)
|
27
|
+
end
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# @api private
|
32
|
+
def errors(context, *options)
|
33
|
+
errors = []
|
34
|
+
iterate_verifiers(*options) do |verifier, verifier_options|
|
35
|
+
verify_one!(context, verifier, verifier_options)
|
36
|
+
rescue ::JWT::DecodeError => e
|
37
|
+
errors << Error.new(message: e.message)
|
38
|
+
end
|
39
|
+
errors
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def iterate_verifiers(*options)
|
45
|
+
options.each do |element|
|
46
|
+
if element.is_a?(Hash)
|
47
|
+
element.each_key { |key| yield(key, element) }
|
48
|
+
else
|
49
|
+
yield(element, {})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def verify_one!(context, verifier, options)
|
55
|
+
verifier_builder = VERIFIERS.fetch(verifier) { raise ArgumentError, "#{verifier} not a valid claim verifier" }
|
56
|
+
verifier_builder.call(options || {}).verify!(context: context)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/jwt/claims.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'claims/audience'
|
4
|
+
require_relative 'claims/crit'
|
5
|
+
require_relative 'claims/decode_verifier'
|
4
6
|
require_relative 'claims/expiration'
|
5
7
|
require_relative 'claims/issued_at'
|
6
8
|
require_relative 'claims/issuer'
|
@@ -9,29 +11,63 @@ require_relative 'claims/not_before'
|
|
9
11
|
require_relative 'claims/numeric'
|
10
12
|
require_relative 'claims/required'
|
11
13
|
require_relative 'claims/subject'
|
14
|
+
require_relative 'claims/verification_methods'
|
15
|
+
require_relative 'claims/verifier'
|
12
16
|
|
13
17
|
module JWT
|
18
|
+
# JWT Claim verifications
|
19
|
+
# https://datatracker.ietf.org/doc/html/rfc7519#section-4
|
20
|
+
#
|
21
|
+
# Verification is supported for the following claims:
|
22
|
+
# exp
|
23
|
+
# nbf
|
24
|
+
# iss
|
25
|
+
# iat
|
26
|
+
# jti
|
27
|
+
# aud
|
28
|
+
# sub
|
29
|
+
# required
|
30
|
+
# numeric
|
14
31
|
module Claims
|
15
|
-
|
16
|
-
|
17
|
-
VERIFIERS = {
|
18
|
-
verify_expiration: ->(options) { Claims::Expiration.new(leeway: options[:exp_leeway] || options[:leeway]) },
|
19
|
-
verify_not_before: ->(options) { Claims::NotBefore.new(leeway: options[:nbf_leeway] || options[:leeway]) },
|
20
|
-
verify_iss: ->(options) { options[:iss] && Claims::Issuer.new(issuers: options[:iss]) },
|
21
|
-
verify_iat: ->(*) { Claims::IssuedAt.new },
|
22
|
-
verify_jti: ->(options) { Claims::JwtId.new(validator: options[:verify_jti]) },
|
23
|
-
verify_aud: ->(options) { options[:aud] && Claims::Audience.new(expected_audience: options[:aud]) },
|
24
|
-
verify_sub: ->(options) { options[:sub] && Claims::Subject.new(expected_subject: options[:sub]) },
|
25
|
-
required_claims: ->(options) { Claims::Required.new(required_claims: options[:required_claims]) }
|
26
|
-
}.freeze
|
32
|
+
# Represents a claim verification error
|
33
|
+
Error = Struct.new(:message, keyword_init: true)
|
27
34
|
|
28
35
|
class << self
|
36
|
+
# @deprecated Use {verify_payload!} instead. Will be removed in the next major version of ruby-jwt.
|
29
37
|
def verify!(payload, options)
|
30
|
-
|
31
|
-
|
38
|
+
Deprecations.warning('The ::JWT::Claims.verify! method is deprecated will be removed in the next major version of ruby-jwt')
|
39
|
+
DecodeVerifier.verify!(payload, options)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Checks if the claims in the JWT payload are valid.
|
43
|
+
# @example
|
44
|
+
#
|
45
|
+
# ::JWT::Claims.verify_payload!({"exp" => Time.now.to_i + 10}, :exp)
|
46
|
+
# ::JWT::Claims.verify_payload!({"exp" => Time.now.to_i - 10}, exp: { leeway: 11})
|
47
|
+
#
|
48
|
+
# @param payload [Hash] the JWT payload.
|
49
|
+
# @param options [Array] the options for verifying the claims.
|
50
|
+
# @return [void]
|
51
|
+
# @raise [JWT::DecodeError] if any claim is invalid.
|
52
|
+
def verify_payload!(payload, *options)
|
53
|
+
Verifier.verify!(VerificationContext.new(payload: payload), *options)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Checks if the claims in the JWT payload are valid.
|
57
|
+
#
|
58
|
+
# @param payload [Hash] the JWT payload.
|
59
|
+
# @param options [Array] the options for verifying the claims.
|
60
|
+
# @return [Boolean] true if the claims are valid, false otherwise
|
61
|
+
def valid_payload?(payload, *options)
|
62
|
+
payload_errors(payload, *options).empty?
|
63
|
+
end
|
32
64
|
|
33
|
-
|
34
|
-
|
65
|
+
# Returns the errors in the claims of the JWT token.
|
66
|
+
#
|
67
|
+
# @param options [Array] the options for verifying the claims.
|
68
|
+
# @return [Array<JWT::Claims::Error>] the errors in the claims of the JWT
|
69
|
+
def payload_errors(payload, *options)
|
70
|
+
Verifier.errors(VerificationContext.new(payload: payload), *options)
|
35
71
|
end
|
36
72
|
end
|
37
73
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
# @deprecated Use `Claims.verify_payload!` directly instead.
|
5
|
+
class ClaimsValidator
|
6
|
+
# @deprecated Use `Claims.verify_payload!` directly instead.
|
7
|
+
def initialize(payload)
|
8
|
+
Deprecations.warning('The ::JWT::ClaimsValidator class is deprecated and will be removed in the next major version of ruby-jwt')
|
9
|
+
@payload = payload
|
10
|
+
end
|
11
|
+
|
12
|
+
# @deprecated Use `Claims.verify_payload!` directly instead.
|
13
|
+
def validate!
|
14
|
+
Claims.verify_payload!(@payload, :numeric)
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -5,14 +5,28 @@ require_relative 'jwk_configuration'
|
|
5
5
|
|
6
6
|
module JWT
|
7
7
|
module Configuration
|
8
|
+
# The Container class holds the configuration settings for JWT.
|
8
9
|
class Container
|
10
|
+
# @!attribute [rw] decode
|
11
|
+
# @return [DecodeConfiguration] the decode configuration.
|
12
|
+
# @!attribute [rw] jwk
|
13
|
+
# @return [JwkConfiguration] the JWK configuration.
|
14
|
+
# @!attribute [rw] strict_base64_decoding
|
15
|
+
# @return [Boolean] whether strict Base64 decoding is enabled.
|
9
16
|
attr_accessor :decode, :jwk, :strict_base64_decoding
|
17
|
+
|
18
|
+
# @!attribute [r] deprecation_warnings
|
19
|
+
# @return [Symbol] the deprecation warnings setting.
|
10
20
|
attr_reader :deprecation_warnings
|
11
21
|
|
22
|
+
# Initializes a new Container instance and resets the configuration.
|
12
23
|
def initialize
|
13
24
|
reset!
|
14
25
|
end
|
15
26
|
|
27
|
+
# Resets the configuration to default values.
|
28
|
+
#
|
29
|
+
# @return [void]
|
16
30
|
def reset!
|
17
31
|
@decode = DecodeConfiguration.new
|
18
32
|
@jwk = JwkConfiguration.new
|
@@ -22,6 +36,12 @@ module JWT
|
|
22
36
|
end
|
23
37
|
|
24
38
|
DEPRECATION_WARNINGS_VALUES = %i[once warn silent].freeze
|
39
|
+
private_constant(:DEPRECATION_WARNINGS_VALUES)
|
40
|
+
# Sets the deprecation warnings setting.
|
41
|
+
#
|
42
|
+
# @param value [Symbol] the deprecation warnings setting. Must be one of `:once`, `:warn`, or `:silent`.
|
43
|
+
# @raise [ArgumentError] if the value is not one of the supported values.
|
44
|
+
# @return [void]
|
25
45
|
def deprecation_warnings=(value)
|
26
46
|
raise ArgumentError, "Invalid deprecation_warnings value #{value}. Supported values: #{DEPRECATION_WARNINGS_VALUES}" unless DEPRECATION_WARNINGS_VALUES.include?(value)
|
27
47
|
|
@@ -2,7 +2,29 @@
|
|
2
2
|
|
3
3
|
module JWT
|
4
4
|
module Configuration
|
5
|
+
# The DecodeConfiguration class holds the configuration settings for decoding JWT tokens.
|
5
6
|
class DecodeConfiguration
|
7
|
+
# @!attribute [rw] verify_expiration
|
8
|
+
# @return [Boolean] whether to verify the expiration claim.
|
9
|
+
# @!attribute [rw] verify_not_before
|
10
|
+
# @return [Boolean] whether to verify the not before claim.
|
11
|
+
# @!attribute [rw] verify_iss
|
12
|
+
# @return [Boolean] whether to verify the issuer claim.
|
13
|
+
# @!attribute [rw] verify_iat
|
14
|
+
# @return [Boolean] whether to verify the issued at claim.
|
15
|
+
# @!attribute [rw] verify_jti
|
16
|
+
# @return [Boolean] whether to verify the JWT ID claim.
|
17
|
+
# @!attribute [rw] verify_aud
|
18
|
+
# @return [Boolean] whether to verify the audience claim.
|
19
|
+
# @!attribute [rw] verify_sub
|
20
|
+
# @return [Boolean] whether to verify the subject claim.
|
21
|
+
# @!attribute [rw] leeway
|
22
|
+
# @return [Integer] the leeway in seconds for time-based claims.
|
23
|
+
# @!attribute [rw] algorithms
|
24
|
+
# @return [Array<String>] the list of acceptable algorithms.
|
25
|
+
# @!attribute [rw] required_claims
|
26
|
+
# @return [Array<String>] the list of required claims.
|
27
|
+
|
6
28
|
attr_accessor :verify_expiration,
|
7
29
|
:verify_not_before,
|
8
30
|
:verify_iss,
|
@@ -14,6 +36,7 @@ module JWT
|
|
14
36
|
:algorithms,
|
15
37
|
:required_claims
|
16
38
|
|
39
|
+
# Initializes a new DecodeConfiguration instance with default settings.
|
17
40
|
def initialize
|
18
41
|
@verify_expiration = true
|
19
42
|
@verify_not_before = true
|
@@ -27,6 +50,7 @@ module JWT
|
|
27
50
|
@required_claims = []
|
28
51
|
end
|
29
52
|
|
53
|
+
# @api private
|
30
54
|
def to_h
|
31
55
|
{
|
32
56
|
verify_expiration: verify_expiration,
|
data/lib/jwt/configuration.rb
CHANGED
@@ -3,11 +3,19 @@
|
|
3
3
|
require_relative 'configuration/container'
|
4
4
|
|
5
5
|
module JWT
|
6
|
+
# The Configuration module provides methods to configure JWT settings.
|
6
7
|
module Configuration
|
8
|
+
# Configures the JWT settings.
|
9
|
+
#
|
10
|
+
# @yield [config] Gives the current configuration to the block.
|
11
|
+
# @yieldparam config [JWT::Configuration::Container] the configuration container.
|
7
12
|
def configure
|
8
13
|
yield(configuration)
|
9
14
|
end
|
10
15
|
|
16
|
+
# Returns the JWT configuration container.
|
17
|
+
#
|
18
|
+
# @return [JWT::Configuration::Container] the configuration container.
|
11
19
|
def configuration
|
12
20
|
@configuration ||= ::JWT::Configuration::Container.new
|
13
21
|
end
|