jwt 2.8.2 → 2.9.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5fc1b13d678680a3d1b23e06cad091deb0f3ffff77783a99cd1d1f7231938563
4
- data.tar.gz: f7d3f5294cb3b6ba57c2772a58c50ee2d55cf6412db6b707a38c74b381662777
3
+ metadata.gz: ce035519b0df530866af63d7dd6cb267490700e9458fc9c32fb0bb15be446f6e
4
+ data.tar.gz: 94fd6a38f0e3c91407b04aa254d568e113877e7dcdcb4b923c28b6a320587119
5
5
  SHA512:
6
- metadata.gz: 5f4602ed313d982db0a2e469c2fc3b58aa0de226f85e332501628d9f7b59ed8a84e78691b57f14ddf153c4be028e9b3caa13d9b3231996c798e9e63f69f4d10a
7
- data.tar.gz: a3ed20caccfe2c2979426b41a6e0bbeeabe67157643c4571836642f48ccf76ba8f58bb3270501eeefcbd53e1d027de1b558310a083360ca644157934ce3c06bf
6
+ metadata.gz: aaf541769af85436646e45a61e0f3a57d75a88e640f4a811eff35d8be61285dc77b1741e79147cf435fed5b51519863fc027722d43805c3457a159d8e235f568
7
+ data.tar.gz: f548c7bcfac5c31520ba0a1cc1b5dd546cf600277667109e7c33fde167138a3a29a930cf127cc51a7546e5b4c14e5e093a8279949107cd0d0062d016067a1723
data/CHANGELOG.md CHANGED
@@ -1,5 +1,71 @@
1
1
  # Changelog
2
2
 
3
+ ## Upcoming breaking changes
4
+
5
+ Notable changes in the upcoming **version 3.0**:
6
+
7
+ - The indirect dependency to [rbnacl](https://github.com/RubyCrypto/rbnacl) will be removed:
8
+ - Support for the nonstandard SHA512256 algorithm will be removed.
9
+ - Support for Ed25519 will be moved to a [separate gem](https://github.com/anakinj/jwt-eddsa) for better dependency handling.
10
+
11
+ - Base64 decoding will no longer fallback on the looser RFC 2045.
12
+
13
+ - Claim verification has been [split into separate classes](https://github.com/jwt/ruby-jwt/pull/605) and has [a new api](https://github.com/jwt/ruby-jwt/pull/626) and lead to the following deprecations:
14
+ - The `::JWT::ClaimsValidator` class will be removed in favor of the functionality provided by `::JWT::Claims`.
15
+ - The `::JWT::Claims::verify!` method will be removed in favor of `::JWT::Claims::verify_payload!`.
16
+ - The `::JWT::JWA.create` method will be removed. No recommended alternatives.
17
+ - The `::JWT::Verify` class will be removed in favor of the functionality provided by `::JWT::Claims`.
18
+ - Calling `::JWT::Claims::Numeric.new` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.
19
+ - Calling `::JWT::Claims::Numeric.verify!` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.
20
+
21
+ - The internal algorithms were [restructured](https://github.com/jwt/ruby-jwt/pull/607) to support extensions from separate libraries. The changes lead to a few deprecations and new requirements:
22
+ - The `sign` and `verify` static methods on all the algorithms (`::JWT::JWA`) will be removed.
23
+ - Custom algorithms are expected to include the `JWT::JWA::SigningAlgorithm` module.
24
+
25
+ ## [v2.9.3](https://github.com/jwt/ruby-jwt/tree/v2.9.3) (2024-10-03)
26
+
27
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.9.2...v2.9.3)
28
+
29
+ **Fixes and enhancements:**
30
+
31
+ - Return truthy value for `::JWT::ClaimsValidator#validate!` and `::JWT::Verify.verify_claims` [#628](https://github.com/jwt/ruby-jwt/pull/628) ([@anakinj](https://github.com/anakinj))
32
+
33
+ ## [v2.9.2](https://github.com/jwt/ruby-jwt/tree/v2.9.2) (2024-10-03)
34
+
35
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.9.1...v2.9.2)
36
+
37
+ **Features:**
38
+
39
+ - Standalone claim verification interface [#626](https://github.com/jwt/ruby-jwt/pull/626) ([@anakinj](https://github.com/anakinj))
40
+
41
+ **Fixes and enhancements:**
42
+
43
+ - Updated README to correctly document `OpenSSL::HMAC` documentation [#617](https://github.com/jwt/ruby-jwt/pull/617) ([@aedryan](https://github.com/aedryan))
44
+ - Verify JWT header format [#622](https://github.com/jwt/ruby-jwt/pull/622) ([@304](https://github.com/304))
45
+ - Bring back `::JWT::ClaimsValidator`, `::JWT::Verify` and a few other removed interfaces for preserved backwards compatibility [#624](https://github.com/jwt/ruby-jwt/pull/624) ([@anakinj](https://github.com/anakinj))
46
+
47
+ ## [v2.9.1](https://github.com/jwt/ruby-jwt/tree/v2.9.1) (2024-09-23)
48
+
49
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.9.0...v2.9.1)
50
+
51
+ **Fixes and enhancements:**
52
+
53
+ - Fix regression in `iss` and `aud` claim validation [#619](https://github.com/jwt/ruby-jwt/pull/619) ([@anakinj](https://github.com/anakinj))
54
+
55
+ ## [v2.9.0](https://github.com/jwt/ruby-jwt/tree/v2.9.0) (2024-09-15)
56
+
57
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.8.2...v2.9.0)
58
+
59
+ **Features:**
60
+
61
+ - Build and push gem using a GH action [#612](https://github.com/jwt/ruby-jwt/pull/612) ([@anakinj](https://github.com/anakinj))
62
+
63
+ **Fixes and enhancements:**
64
+
65
+ - Refactor claim validators into their own classes [#605](https://github.com/jwt/ruby-jwt/pull/605) ([@anakinj](https://github.com/anakinj), [@MatteoPierro](https://github.com/MatteoPierro))
66
+ - Allow extending available algorithms [#607](https://github.com/jwt/ruby-jwt/pull/607) ([@anakinj](https://github.com/anakinj))
67
+ - Do not include the EdDSA algorithm if rbnacl not available [#613](https://github.com/jwt/ruby-jwt/pull/613) ([@anakinj](https://github.com/anakinj))
68
+
3
69
  ## [v2.8.2](https://github.com/jwt/ruby-jwt/tree/v2.8.2) (2024-06-18)
4
70
 
5
71
  [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.8.1...v2.8.2)
data/README.md CHANGED
@@ -223,20 +223,24 @@ puts decoded_token
223
223
 
224
224
  ### **Custom algorithms**
225
225
 
226
- An object implementing custom signing or verification behaviour can be passed in the `algorithm` option when encoding and decoding. The given object needs to implement the method `valid_alg?` and `verify` and/or `alg` and `sign`, depending if object is used for encoding or decoding.
226
+ When encoding or decoding a token, you can pass in a custom object through the `algorithm` option to handle signing or verification. This custom object must include or extend the `JWT::JWA::SigningAlgorithm` module and implement certain methods:
227
+
228
+ - For decoding/verifying: The object must implement the methods `alg` and `verify`.
229
+ - For encoding/signing: The object must implement the methods `alg` and `sign`.
230
+
231
+ For customization options check the details from `JWT::JWA::SigningAlgorithm`.
232
+
227
233
 
228
234
  ```ruby
229
235
  module CustomHS512Algorithm
236
+ extend JWT::JWA::SigningAlgorithm
237
+
230
238
  def self.alg
231
239
  'HS512'
232
240
  end
233
241
 
234
- def self.valid_alg?(alg_to_validate)
235
- alg_to_validate == alg
236
- end
237
-
238
242
  def self.sign(data:, signing_key:)
239
- OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha512'), data, signing_key)
243
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha512'), signing_key, data)
240
244
  end
241
245
 
242
246
  def self.verify(data:, signature:, verification_key:)
@@ -526,6 +530,24 @@ rescue JWT::InvalidSubError
526
530
  end
527
531
  ```
528
532
 
533
+ ### Standalone claim verification
534
+
535
+ The JWT claim verifications can be used to verify any Hash to include expected keys and values.
536
+
537
+ A few example on verifying the claims for a payload:
538
+ ```ruby
539
+ JWT::Claims.verify_payload!({"exp" => Time.now.to_i + 10}, :numeric, :exp)
540
+ JWT::Claims.valid_payload?({"exp" => Time.now.to_i + 10}, :exp)
541
+ # => true
542
+ JWT::Claims.payload_errors({"exp" => Time.now.to_i - 10}, :exp)
543
+ # => [#<struct JWT::Claims::Error message="Signature has expired">]
544
+ JWT::Claims.verify_payload!({"exp" => Time.now.to_i - 10}, exp: { leeway: 11})
545
+
546
+ JWT::Claims.verify_payload!({"exp" => Time.now.to_i + 10, "sub" => "subject"}, :exp, sub: "subject")
547
+ ```
548
+
549
+
550
+
529
551
  ### Finding a Key
530
552
 
531
553
  To dynamically find the key for verifying the JWT signature, pass a block to the decode block. The block receives headers and the original payload as parameters. It should return with the key to verify the signature that was used to sign the JWT.
@@ -690,21 +712,25 @@ jwk_hash = jwk.export
690
712
  thumbprint_as_the_kid = jwk_hash[:kid]
691
713
  ```
692
714
 
693
- # Development and Tests
715
+ # Development and testing
694
716
 
695
- We depend on [Bundler](http://rubygems.org/gems/bundler) for defining gemspec and performing releases to rubygems.org, which can be done with
717
+ The tests are written with rspec. [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
696
718
 
697
719
  ```bash
698
- rake release
720
+ bundle install
721
+ bundle exec appraisal rake test
699
722
  ```
700
723
 
701
- The tests are written with rspec. [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
724
+ # Releasing
725
+
726
+ To cut a new release adjust the [version.rb](lib/jwt/version.rb) and [CHANGELOG](CHANGELOG.md) with desired version numbers and dates and commit the changes. Tag the release with the version number using the following command:
702
727
 
703
728
  ```bash
704
- bundle install
705
- bundle exec appraisal rake test
729
+ rake release:source_control_push
706
730
  ```
707
731
 
732
+ This will tag a new version an trigger a [GitHub action](.github/workflows/push_gem.yml) that eventually will push the gem to rubygems.org.
733
+
708
734
  ## How to contribute
709
735
  See [CONTRIBUTING](CONTRIBUTING.md).
710
736
 
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Audience
6
+ def initialize(expected_audience:)
7
+ @expected_audience = expected_audience
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ aud = context.payload['aud']
12
+ raise JWT::InvalidAudError, "Invalid audience. Expected #{expected_audience}, received #{aud || '<none>'}" if ([*aud] & [*expected_audience]).empty?
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :expected_audience
18
+ end
19
+ end
20
+ 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
+ # @private
8
+ VerificationContext = Struct.new(:payload, keyword_init: true)
9
+
10
+ # Verifiers to support the ::JWT.decode method
11
+ #
12
+ # @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
+ # @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
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Expiration
6
+ def initialize(leeway:)
7
+ @leeway = leeway || 0
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ return unless context.payload.is_a?(Hash)
12
+ return unless context.payload.key?('exp')
13
+
14
+ raise JWT::ExpiredSignature, 'Signature has expired' if context.payload['exp'].to_i <= (Time.now.to_i - leeway)
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :leeway
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class IssuedAt
6
+ def verify!(context:, **_args)
7
+ return unless context.payload.is_a?(Hash)
8
+ return unless context.payload.key?('iat')
9
+
10
+ iat = context.payload['iat']
11
+ raise(JWT::InvalidIatError, 'Invalid iat') if !iat.is_a?(::Numeric) || iat.to_f > Time.now.to_f
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Issuer
6
+ def initialize(issuers:)
7
+ @issuers = Array(issuers).map { |item| item.is_a?(Symbol) ? item.to_s : item }
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ case (iss = context.payload['iss'])
12
+ when *issuers
13
+ nil
14
+ else
15
+ raise JWT::InvalidIssuerError, "Invalid issuer. Expected #{issuers}, received #{iss || '<none>'}"
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :issuers
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class JwtId
6
+ def initialize(validator:)
7
+ @validator = validator
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ jti = context.payload['jti']
12
+ if validator.respond_to?(:call)
13
+ verified = validator.arity == 2 ? validator.call(jti, context.payload) : validator.call(jti)
14
+ raise(JWT::InvalidJtiError, 'Invalid jti') unless verified
15
+ elsif jti.to_s.strip.empty?
16
+ raise(JWT::InvalidJtiError, 'Missing jti')
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :validator
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class NotBefore
6
+ def initialize(leeway:)
7
+ @leeway = leeway || 0
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ return unless context.payload.is_a?(Hash)
12
+ return unless context.payload.key?('nbf')
13
+
14
+ raise JWT::ImmatureSignature, 'Signature nbf has not been reached' if context.payload['nbf'].to_i > (Time.now.to_i + leeway)
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :leeway
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Numeric
6
+ class Compat
7
+ def initialize(payload)
8
+ @payload = payload
9
+ end
10
+
11
+ def verify!
12
+ JWT::Claims.verify_payload!(@payload, :numeric)
13
+ end
14
+ end
15
+
16
+ NUMERIC_CLAIMS = %i[
17
+ exp
18
+ iat
19
+ nbf
20
+ ].freeze
21
+
22
+ def self.new(*args)
23
+ return super if args.empty?
24
+
25
+ Compat.new(*args)
26
+ end
27
+
28
+ def verify!(context:)
29
+ validate_numeric_claims(context.payload)
30
+ end
31
+
32
+ def self.verify!(payload:, **_args)
33
+ JWT::Claims.verify_payload!(payload, :numeric)
34
+ end
35
+
36
+ private
37
+
38
+ def validate_numeric_claims(payload)
39
+ NUMERIC_CLAIMS.each do |claim|
40
+ validate_is_numeric(payload, claim)
41
+ end
42
+ end
43
+
44
+ def validate_is_numeric(payload, claim)
45
+ return unless payload.is_a?(Hash)
46
+ return unless payload.key?(claim) ||
47
+ payload.key?(claim.to_s)
48
+
49
+ return if payload[claim].is_a?(::Numeric) || payload[claim.to_s].is_a?(::Numeric)
50
+
51
+ raise InvalidPayload, "#{claim} claim must be a Numeric value but it is a #{(payload[claim] || payload[claim.to_s]).class}"
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Required
6
+ def initialize(required_claims:)
7
+ @required_claims = required_claims
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ required_claims.each do |required_claim|
12
+ next if context.payload.is_a?(Hash) && context.payload.key?(required_claim)
13
+
14
+ raise JWT::MissingRequiredClaim, "Missing required claim #{required_claim}"
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :required_claims
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Subject
6
+ def initialize(expected_subject:)
7
+ @expected_subject = expected_subject.to_s
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ sub = context.payload['sub']
12
+ raise(JWT::InvalidSubError, "Invalid subject. Expected #{expected_subject}, received #{sub || '<none>'}") unless sub.to_s == expected_subject
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :expected_subject
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ # @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
+
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
+ # @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
+ # @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
+ def iterate_verifiers(*options)
44
+ options.each do |element|
45
+ if element.is_a?(Hash)
46
+ element.each_key { |key| yield(key, element) }
47
+ else
48
+ yield(element, {})
49
+ end
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def verify_one!(context, verifier, options)
56
+ verifier_builder = VERIFIERS.fetch(verifier) { raise ArgumentError, "#{verifier} not a valid claim verifier" }
57
+ verifier_builder.call(options || {}).verify!(context: context)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
data/lib/jwt/claims.rb ADDED
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'claims/audience'
4
+ require_relative 'claims/expiration'
5
+ require_relative 'claims/issued_at'
6
+ require_relative 'claims/issuer'
7
+ require_relative 'claims/jwt_id'
8
+ require_relative 'claims/not_before'
9
+ require_relative 'claims/numeric'
10
+ require_relative 'claims/required'
11
+ require_relative 'claims/subject'
12
+ require_relative 'claims/decode_verifier'
13
+ require_relative 'claims/verifier'
14
+
15
+ module JWT
16
+ # JWT Claim verifications
17
+ # https://datatracker.ietf.org/doc/html/rfc7519#section-4
18
+ #
19
+ # Verification is supported for the following claims:
20
+ # exp
21
+ # nbf
22
+ # iss
23
+ # iat
24
+ # jti
25
+ # aud
26
+ # sub
27
+ # required
28
+ # numeric
29
+ #
30
+ module Claims
31
+ # Represents a claim verification error
32
+ Error = Struct.new(:message, keyword_init: true)
33
+
34
+ class << self
35
+ # @deprecated Use {verify_payload!} instead. Will be removed in the next major version of ruby-jwt.
36
+ def verify!(payload, options)
37
+ DecodeVerifier.verify!(payload, options)
38
+ end
39
+
40
+ # Checks if the claims in the JWT payload are valid.
41
+ # @example
42
+ #
43
+ # ::JWT::Claims.verify_payload!({"exp" => Time.now.to_i + 10}, :exp)
44
+ # ::JWT::Claims.verify_payload!({"exp" => Time.now.to_i - 10}, exp: { leeway: 11})
45
+ #
46
+ # @param payload [Hash] the JWT payload.
47
+ # @param options [Array] the options for verifying the claims.
48
+ # @return [void]
49
+ # @raise [JWT::DecodeError] if any claim is invalid.
50
+ def verify_payload!(payload, *options)
51
+ verify_token!(VerificationContext.new(payload: payload), *options)
52
+ end
53
+
54
+ # Checks if the claims in the JWT payload are valid.
55
+ #
56
+ # @param payload [Hash] the JWT payload.
57
+ # @param options [Array] the options for verifying the claims.
58
+ # @return [Boolean] true if the claims are valid, false otherwise
59
+ def valid_payload?(payload, *options)
60
+ payload_errors(payload, *options).empty?
61
+ end
62
+
63
+ # Returns the errors in the claims of the JWT token.
64
+ #
65
+ # @param options [Array] the options for verifying the claims.
66
+ # @return [Array<JWT::Claims::Error>] the errors in the claims of the JWT
67
+ def payload_errors(payload, *options)
68
+ token_errors(VerificationContext.new(payload: payload), *options)
69
+ end
70
+
71
+ private
72
+
73
+ def verify_token!(token, *options)
74
+ Verifier.verify!(token, *options)
75
+ end
76
+
77
+ def token_errors(token, *options)
78
+ Verifier.errors(token, *options)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -4,34 +4,13 @@ require_relative 'error'
4
4
 
5
5
  module JWT
6
6
  class ClaimsValidator
7
- NUMERIC_CLAIMS = %i[
8
- exp
9
- iat
10
- nbf
11
- ].freeze
12
-
13
7
  def initialize(payload)
14
- @payload = payload.transform_keys(&:to_sym)
8
+ @payload = payload
15
9
  end
16
10
 
17
11
  def validate!
18
- validate_numeric_claims
19
-
12
+ Claims.verify_payload!(@payload, :numeric)
20
13
  true
21
14
  end
22
-
23
- private
24
-
25
- def validate_numeric_claims
26
- NUMERIC_CLAIMS.each do |claim|
27
- validate_is_numeric(claim) if @payload.key?(claim)
28
- end
29
- end
30
-
31
- def validate_is_numeric(claim)
32
- return if @payload[claim].is_a?(Numeric)
33
-
34
- raise InvalidPayload, "#{claim} claim must be a Numeric value but it is a #{@payload[claim].class}"
35
- end
36
15
  end
37
16
  end
data/lib/jwt/decode.rb CHANGED
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
-
5
- require 'jwt/verify'
6
4
  require 'jwt/x5c_key_finder'
7
5
 
8
6
  # JWT::Decode module
@@ -51,6 +49,7 @@ module JWT
51
49
 
52
50
  def verify_algo
53
51
  raise JWT::IncorrectAlgorithm, 'An algorithm must be specified' if allowed_algorithms.empty?
52
+ raise JWT::DecodeError, 'Token header not a JSON object' unless header.is_a?(Hash)
54
53
  raise JWT::IncorrectAlgorithm, 'Token is missing alg header' unless alg_in_header
55
54
  raise JWT::IncorrectAlgorithm, 'Expected a different algorithm' if allowed_and_valid_algorithms.empty?
56
55
  end
@@ -92,7 +91,7 @@ module JWT
92
91
  end
93
92
 
94
93
  def resolve_allowed_algorithms
95
- algs = given_algorithms.map { |alg| JWA.create(alg) }
94
+ algs = given_algorithms.map { |alg| JWA.resolve(alg) }
96
95
 
97
96
  sort_by_alg_header(algs)
98
97
  end
@@ -113,8 +112,7 @@ module JWT
113
112
  end
114
113
 
115
114
  def verify_claims
116
- Verify.verify_claims(payload, @options)
117
- Verify.verify_required_claims(payload, @options)
115
+ Claims::DecodeVerifier.verify!(payload, @options)
118
116
  end
119
117
 
120
118
  def validate_segment_count!