jwt 2.10.1 → 3.0.0.beta1

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: dcc16f3a25f01facd96faaf83722fd6d45e2a2fa80539e68727cee1a6df71cc1
4
- data.tar.gz: 241e7ef393bd3c40356e730466e32d45bc63f0d4e9983d2c40c7bef2424334fa
3
+ metadata.gz: 30b9373b7591af7e5e8ba0f049a2d41bc87afe0ea21462bd011f9558ec45e892
4
+ data.tar.gz: d5e77784a5eda9bae03f7bed74dabcfa81db1720057f742787c723d679d1005c
5
5
  SHA512:
6
- metadata.gz: dffc0046d44c6a5d03538bbd9f0870da9142873ea5ffbb186ccfd339324d3e4c0c1f2e104c668f819047f54573717cd396b1bf8cb96a9a971cf02f6151100bfe
7
- data.tar.gz: d86d34077d0fe9d760d72bd176262fafedcea294dada4e95f33b2a0bbeb9995f8e9e3f8d6225cff3cdd480d981dfa40da396683a662cdabc9410266fbde0709f
6
+ metadata.gz: eafdd62f2eb9c35d2a27bf0429a866dd6f54b56c2650c373185f1c3e53ce08e06d03e5d8baba934493ea615cc9b86038d93a634509e5d932fdcbc91d669310e9
7
+ data.tar.gz: 68b004a6d858e2bfb78ba41017b19ec37b0dbed43525f63b9a743111f6d2f26eb4aea7a61ff2d2774645c504021d34ee8296cf342f36af97c426e96690ec08d7
data/CHANGELOG.md CHANGED
@@ -1,10 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.0.0](https://github.com/jwt/ruby-jwt/tree/v3.0.0) (NEXT)
4
+
5
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.10.1...main)
6
+
7
+ **Breaking changes:**
8
+ - Require token signature to be verified before accessing payload [#648](https://github.com/jwt/ruby-jwt/pull/648) ([@anakinj](https://github.com/anakinj))
9
+ - Drop support for the HS512256 algorithm [#650](https://github.com/jwt/ruby-jwt/pull/650) ([@anakinj](https://github.com/anakinj))
10
+ - Remove deprecated claim verification methods [#654](https://github.com/jwt/ruby-jwt/pull/654) ([@anakinj](https://github.com/anakinj))
11
+ - Remove dependency to rbnacl [#655](https://github.com/jwt/ruby-jwt/pull/655) ([@anakinj](https://github.com/anakinj))
12
+ - Support only stricter base64 decoding (RFC 4648) [#658](https://github.com/jwt/ruby-jwt/pull/658) ([@anakinj](https://github.com/anakinj))
13
+ - Custom algorithms are required to include `JWT::JWA::SigningAlgorithm` [#660](https://github.com/jwt/ruby-jwt/pull/660) ([@anakinj](https://github.com/anakinj))
14
+ - Require RSA keys to be at least 2048 bits [#661](https://github.com/jwt/ruby-jwt/pull/661) ([@anakinj](https://github.com/anakinj))
15
+ - Base64 encode and decode the k value for HMAC JWKs [#662](https://github.com/jwt/ruby-jwt/pull/662) ([@anakinj](https://github.com/anakinj))
16
+
17
+ Take a look at the [upgrade guide](UPGRADING.md) for more details.
18
+
19
+ **Features:**
20
+ - JWT::EncodedToken#verify! method that bundles signature and claim validation [#647](https://github.com/jwt/ruby-jwt/pull/647) ([@anakinj](https://github.com/anakinj))
21
+ - Do not override the alg header if already given [#659](https://github.com/jwt/ruby-jwt/pull/659) ([@anakinj](https://github.com/anakinj))
22
+ - Make `JWK::KeyFinder` compatible with `JWT::EncodedToken` [#663](https://github.com/jwt/ruby-jwt/pull/663) ([@anakinj](https://github.com/anakinj))
23
+ - Your contribution here
24
+
25
+ **Fixes and enhancements:**
26
+
27
+ - Ruby 3.4 to CI matrix [#649](https://github.com/jwt/ruby-jwt/pull/649) ([@anakinj](https://github.com/anakinj))
28
+ - Your contribution here
29
+
3
30
  ## [v2.10.1](https://github.com/jwt/ruby-jwt/tree/v2.10.1) (2024-12-26)
4
31
 
32
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.10.0...v2.10.1)
33
+
5
34
  **Fixes and enhancements:**
6
35
 
7
- - Make version constants public again [#646](https://github.com/jwt/ruby-jwt/pull/646) ([@anakinj]
36
+ - Make version constants public again [#646](https://github.com/jwt/ruby-jwt/pull/646) ([@anakinj](https://github.com/anakinj))
8
37
 
9
38
  ## [v2.10.0](https://github.com/jwt/ruby-jwt/tree/v2.10.0) (2024-12-25)
10
39
 
data/README.md CHANGED
@@ -14,25 +14,7 @@ See [CHANGELOG.md](CHANGELOG.md) for a complete set of changes.
14
14
 
15
15
  ## Upcoming breaking changes
16
16
 
17
- Notable changes in the upcoming **version 3.0**:
18
-
19
- - The indirect dependency to [rbnacl](https://github.com/RubyCrypto/rbnacl) will be removed:
20
- - Support for the non-standard SHA512256 algorithm will be removed.
21
- - Support for Ed25519 will be moved to a [separate gem](https://github.com/anakinj/jwt-eddsa) for better dependency handling.
22
-
23
- - Base64 decoding will no longer fallback on the looser RFC 2045.
24
-
25
- - 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:
26
- - The `::JWT::ClaimsValidator` class will be removed in favor of the functionality provided by `::JWT::Claims`.
27
- - The `::JWT::Claims::verify!` method will be removed in favor of `::JWT::Claims::verify_payload!`.
28
- - The `::JWT::JWA.create` method will be removed.
29
- - The `::JWT::Verify` class will be removed in favor of the functionality provided by `::JWT::Claims`.
30
- - Calling `::JWT::Claims::Numeric.new` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.
31
- - Calling `::JWT::Claims::Numeric.verify!` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.
32
-
33
- - 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:
34
- - The `sign` and `verify` static methods on all the algorithms (`::JWT::JWA`) will be removed.
35
- - Custom algorithms are expected to include the `JWT::JWA::SigningAlgorithm` module.
17
+ Check out breaking changes in the upcoming **version 3.0** from the [upgrade guide](UPGRADING.md)
36
18
 
37
19
  ## Sponsors
38
20
 
@@ -189,49 +171,18 @@ decoded_token = JWT.decode(token, ecdsa_key, true, { algorithm: 'ES256' })
189
171
  puts decoded_token
190
172
  ```
191
173
 
192
- ### **EDDSA**
193
-
194
- In order to use this algorithm you need to add the `RbNaCl` gem to you `Gemfile`.
195
-
196
- ```ruby
197
- gem 'rbnacl'
198
- ```
199
-
200
- For more detailed installation instruction check the official [repository](https://github.com/RubyCrypto/rbnacl) on GitHub.
201
-
202
- * ED25519
203
-
204
- ```ruby
205
- private_key = RbNaCl::Signatures::Ed25519::SigningKey.new('abcdefghijklmnopqrstuvwxyzABCDEF')
206
- public_key = private_key.verify_key
207
- token = JWT.encode payload, private_key, 'ED25519'
208
-
209
- # eyJhbGciOiJFRDI1NTE5In0.eyJkYXRhIjoidGVzdCJ9.6xIztXyOupskddGA_RvKU76V9b2dCQUYhoZEVFnRimJoPYIzZ2Fm47CWw8k2NTCNpgfAuxg9OXjaiVK7MvrbCQ
210
- puts token
174
+ ### **EdDSA**
211
175
 
212
- decoded_token = JWT.decode token, public_key, true, { algorithm: 'ED25519' }
213
- # Array
214
- # [
215
- # {"test"=>"data"}, # payload
216
- # {"alg"=>"ED25519"} # header
217
- # ]
218
-
219
- ```
176
+ This algorithm has since version 3.0 been moved to the [jwt-eddsa](https://rubygems.org/gems/jwt-eddsa) gem.
220
177
 
221
178
  ### **RSASSA-PSS**
222
179
 
223
- In order to use this algorithm you need to add the `openssl` gem to your `Gemfile` with a version greater or equal to `2.1`.
224
-
225
- ```ruby
226
- gem 'openssl', '~> 2.1'
227
- ```
228
-
229
180
  * PS256 - RSASSA-PSS using SHA-256 hash algorithm
230
181
  * PS384 - RSASSA-PSS using SHA-384 hash algorithm
231
182
  * PS512 - RSASSA-PSS using SHA-512 hash algorithm
232
183
 
233
184
  ```ruby
234
- rsa_private = OpenSSL::PKey::RSA.generate 2048
185
+ rsa_private = OpenSSL::PKey::RSA.generate(2048)
235
186
  rsa_public = rsa_private.public_key
236
187
 
237
188
  token = JWT.encode(payload, rsa_private, 'PS256')
@@ -254,7 +205,7 @@ Ruby-jwt gem supports custom [header fields](https://tools.ietf.org/html/rfc7519
254
205
  To add custom header fields you need to pass `header_fields` parameter
255
206
 
256
207
  ```ruby
257
- token = JWT.encode(payload, key, algorithm='HS256', header_fields={})
208
+ token = JWT.encode(payload, key, 'HS256', header_fields={})
258
209
  ```
259
210
 
260
211
  **Example:**
@@ -335,6 +286,45 @@ encoded_token.payload # => { 'exp'=>1234, 'jti'=>'1234", 'sub'=>'my-subject' }
335
286
  encoded_token.header # {'kid'=>'hmac', 'alg'=>'HS256'}
336
287
  ```
337
288
 
289
+ The `JWT::EncodedToken#verify!` method can be used to verify signature and claim verification in one go. The `exp` claim is verified by default.
290
+
291
+ ```ruby
292
+ encoded_token = JWT::EncodedToken.new(token.jwt)
293
+ encoded_token.verify!(signature: {algorithm: 'HS256', key: "secret"})
294
+
295
+ encoded_token.payload # => { 'exp'=>1234, 'jti'=>'1234", 'sub'=>'my-subject' }
296
+ encoded_token.header # {'kid'=>'hmac', 'alg'=>'HS256'}
297
+ ```
298
+
299
+ A keyfinder can be used to verify a signature. A keyfinder is an object responding to the `#call` method. The method expects to receive one argument, which is the token to be verified.
300
+
301
+ An example on using the built-in JWK keyfinder:
302
+ ```ruby
303
+ # Create and sign a token
304
+ jwk = JWT::JWK.new(OpenSSL::PKey::RSA.generate(2048))
305
+ token = JWT::Token.new(payload: { pay: 'load' }, header: { kid: jwk.kid })
306
+ token.sign!(algorithm: 'RS256', key: jwk.signing_key)
307
+
308
+ # Create keyfinder object, verify and decode token
309
+ key_finder = JWT::JWK::KeyFinder.new(jwks: JWT::JWK::Set.new(jwk))
310
+ encoded_token = JWT::EncodedToken.new(token.jwt)
311
+ encoded_token.verify!(signature: { algorithm: 'RS256', key_finder: key_finder})
312
+ encoded_token.payload # => { 'pay' => 'load' }
313
+ ```
314
+
315
+ Using a custom keyfinder proc:
316
+ ```ruby
317
+ # Create and sign a token
318
+ key = OpenSSL::PKey::RSA.generate(2048)
319
+ token = JWT::Token.new(payload: { pay: 'load' })
320
+ token.sign!(algorithm: 'RS256', key: key)
321
+
322
+ # Verify and decode token
323
+ encoded_token = JWT::EncodedToken.new(token.jwt)
324
+ encoded_token.verify!(signature: { algorithm: 'RS256', key_finder: ->(_token){ key.public_key }})
325
+ encoded_token.payload # => { 'pay' => 'load' }
326
+ ```
327
+
338
328
  ### Detached payload
339
329
 
340
330
  The `::JWT::Token#detach_payload!` method can be use to detach the payload from the JWT.
data/UPGRADING.md ADDED
@@ -0,0 +1,46 @@
1
+ # Upgrading ruby-jwt to >= 3.0.0
2
+
3
+ ## Removal of the indirect [RbNaCl](https://github.com/RubyCrypto/rbnacl) dependency
4
+
5
+ Historically, the set of supported algorithms was extended by including the `rbnacl` gem in the application's Gemfile. On load, ruby-jwt tried to load the gem and, if available, extend the algorithms to those provided by the `rbnacl/libsodium` libraries. This indirect dependency has caused some maintenance pain and confusion about which versions of the gem are supported.
6
+
7
+ Some work to ease the way alternative algorithms can be implemented has been done. This enables the extraction of the algorithm provided by `rbnacl`.
8
+
9
+ The extracted algorithms now live in the [jwt-eddsa](https://rubygems.org/gems/jwt-eddsa) gem.
10
+
11
+ ### Dropped support for HS512256
12
+
13
+ The algorithm HS512256 (HMAC-SHA-512 truncated to 256-bits) is not part of any JWA/JWT RFC and therefore will not be supported anymore. It was part of the HMAC algorithms provided by the indirect [RbNaCl](https://github.com/RubyCrypto/rbnacl) dependency. Currently, there are no direct substitutes for the algorithm.
14
+
15
+ ### `JWT::EncodedToken#payload` will raise before token is verified
16
+
17
+ To avoid accidental use of unverified tokens, the `JWT::EncodedToken#payload` method will raise an error if accessed before the token signature has been verified.
18
+
19
+ To access the payload before verification, use the method `JWT::EncodedToken#unverified_payload`.
20
+
21
+ ## Stricter requirements on Base64 encoded data
22
+
23
+ Base64 decoding will no longer fallback on the looser RFC 2045. The biggest difference is that the looser version was ignoring whitespaces and newlines, whereas the stricter version raises errors in such cases.
24
+
25
+ If you, for example, read tokens from files, there could be problems with trailing newlines. Make sure you trim your input before passing it to the decoding mechanisms.
26
+
27
+ ## Claim verification revamp
28
+
29
+ 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), leading to the following deprecations:
30
+
31
+ - The `::JWT::ClaimsValidator` class will be removed in favor of the functionality provided by `::JWT::Claims`.
32
+ - The `::JWT::Claims::verify!` method will be removed in favor of `::JWT::Claims::verify_payload!`.
33
+ - The `::JWT::JWA.create` method will be removed.
34
+ - The `::JWT::Verify` class will be removed in favor of the functionality provided by `::JWT::Claims`.
35
+ - Calling `::JWT::Claims::Numeric.new` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.
36
+ - Calling `::JWT::Claims::Numeric.verify!` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.
37
+
38
+ ## Algorithm restructuring
39
+
40
+ The internal algorithms were [restructured](https://github.com/jwt/ruby-jwt/pull/607) to support extensions from separate libraries. The changes led to a few deprecations and new requirements:
41
+ - The `sign` and `verify` static methods on all the algorithms (`::JWT::JWA`) will be removed.
42
+ - Custom algorithms are expected to include the `JWT::JWA::SigningAlgorithm` module.
43
+
44
+ ## Base64 the `k´ value for HMAC JWKs
45
+
46
+ The gem was missing the Base64 encoding and decoding when representing and parsing a HMAC key as a JWK. This issue is now addressed. The added encoding will break compatibility with JWKs produced by older versions of the gem.
data/lib/jwt/base64.rb CHANGED
@@ -14,22 +14,13 @@ module JWT
14
14
  end
15
15
 
16
16
  # Decode a string with URL-safe Base64 complying with RFC 4648.
17
- # Deprecated support for RFC 2045 remains for now. ("All line breaks or other characters not found in Table 1 must be ignored by decoding software")
18
17
  # @api private
19
18
  def url_decode(str)
20
19
  ::Base64.urlsafe_decode64(str)
21
20
  rescue ArgumentError => e
22
21
  raise unless e.message == 'invalid base64'
23
- raise Base64DecodeError, 'Invalid base64 encoding' if JWT.configuration.strict_base64_decoding
24
22
 
25
- loose_urlsafe_decode64(str).tap do
26
- Deprecations.warning('Invalid base64 input detected, could be because of invalid padding, trailing whitespaces or newline chars. Graceful handling of invalid input will be dropped in the next major version of ruby-jwt', only_if_valid: true)
27
- end
28
- end
29
-
30
- def loose_urlsafe_decode64(str)
31
- str += '=' * (4 - str.length.modulo(4))
32
- ::Base64.decode64(str.tr('-_', '+/'))
23
+ raise Base64DecodeError, 'Invalid base64 encoding'
33
24
  end
34
25
  end
35
26
  end
@@ -5,18 +5,6 @@ module JWT
5
5
  # The Numeric class is responsible for validating numeric claims in a JWT token.
6
6
  # The numeric claims are: exp, iat and nbf
7
7
  class Numeric
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
14
-
15
- def verify!
16
- JWT::Claims.verify_payload!(@payload, :numeric)
17
- end
18
- end
19
-
20
8
  # List of numeric claims that can be validated.
21
9
  NUMERIC_CLAIMS = %i[
22
10
  exp
@@ -26,14 +14,6 @@ module JWT
26
14
 
27
15
  private_constant(:NUMERIC_CLAIMS)
28
16
 
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)
35
- end
36
-
37
17
  # Verifies the numeric claims in the JWT context.
38
18
  #
39
19
  # @param context [Object] the context containing the JWT payload.
@@ -43,18 +23,6 @@ module JWT
43
23
  validate_numeric_claims(context.payload)
44
24
  end
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)
56
- end
57
-
58
26
  private
59
27
 
60
28
  def validate_numeric_claims(payload)
data/lib/jwt/claims.rb CHANGED
@@ -11,7 +11,6 @@ require_relative 'claims/not_before'
11
11
  require_relative 'claims/numeric'
12
12
  require_relative 'claims/required'
13
13
  require_relative 'claims/subject'
14
- require_relative 'claims/verification_methods'
15
14
  require_relative 'claims/verifier'
16
15
 
17
16
  module JWT
@@ -33,12 +32,6 @@ module JWT
33
32
  Error = Struct.new(:message, keyword_init: true)
34
33
 
35
34
  class << self
36
- # @deprecated Use {verify_payload!} instead. Will be removed in the next major version of ruby-jwt.
37
- def verify!(payload, options)
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
35
  # Checks if the claims in the JWT payload are valid.
43
36
  # @example
44
37
  #
@@ -30,7 +30,6 @@ module JWT
30
30
  def reset!
31
31
  @decode = DecodeConfiguration.new
32
32
  @jwk = JwkConfiguration.new
33
- @strict_base64_decoding = false
34
33
 
35
34
  self.deprecation_warnings = :once
36
35
  end
data/lib/jwt/decode.rb CHANGED
@@ -33,10 +33,10 @@ module JWT
33
33
  verify_algo
34
34
  set_key
35
35
  verify_signature
36
- Claims::DecodeVerifier.verify!(token.payload, @options)
36
+ Claims::DecodeVerifier.verify!(token.unverified_payload, @options)
37
37
  end
38
38
 
39
- [token.payload, token.header]
39
+ [token.unverified_payload, token.header]
40
40
  end
41
41
 
42
42
  private
@@ -77,11 +77,8 @@ module JWT
77
77
  :algorithms].freeze
78
78
 
79
79
  def given_algorithms
80
- ALGORITHM_KEYS.each do |alg_key|
81
- alg = @options[alg_key]
82
- return Array(alg) if alg
83
- end
84
- []
80
+ alg_key = ALGORITHM_KEYS.find { |key| @options[key] }
81
+ Array(@options[alg_key])
85
82
  end
86
83
 
87
84
  def allowed_algorithms
@@ -93,7 +90,7 @@ module JWT
93
90
  end
94
91
 
95
92
  def find_key(&keyfinder)
96
- key = (keyfinder.arity == 2 ? yield(token.header, token.payload) : yield(token.header))
93
+ key = (keyfinder.arity == 2 ? yield(token.header, token.unverified_payload) : yield(token.header))
97
94
  # key can be of type [string, nil, OpenSSL::PKey, Array]
98
95
  return key if key && !Array(key).empty?
99
96
 
@@ -12,7 +12,21 @@ module JWT
12
12
  # encoded_token.verify_signature!(algorithm: 'HS256', key: 'secret')
13
13
  # encoded_token.payload # => {'pay' => 'load'}
14
14
  class EncodedToken
15
- include Claims::VerificationMethods
15
+ # @private
16
+ # Allow access to the unverified payload for claim verification.
17
+ class ClaimsContext
18
+ extend Forwardable
19
+
20
+ def_delegators :@token, :header, :unverified_payload
21
+
22
+ def initialize(token)
23
+ @token = token
24
+ end
25
+
26
+ def payload
27
+ unverified_payload
28
+ end
29
+ end
16
30
 
17
31
  # Returns the original token provided to the class.
18
32
  # @return [String] The JWT token.
@@ -26,6 +40,7 @@ module JWT
26
40
  raise ArgumentError, 'Provided JWT must be a String' unless jwt.is_a?(String)
27
41
 
28
42
  @jwt = jwt
43
+ @signature_verified = false
29
44
  @encoded_header, @encoded_payload, @encoded_signature = jwt.split('.')
30
45
  end
31
46
 
@@ -53,11 +68,20 @@ module JWT
53
68
  # @return [String] the encoded header.
54
69
  attr_reader :encoded_header
55
70
 
56
- # Returns the payload of the JWT token.
71
+ # Returns the payload of the JWT token. Access requires the signature to have been verified.
57
72
  #
58
73
  # @return [Hash] the payload.
74
+ # @raise [JWT::DecodeError] if the signature has not been verified.
59
75
  def payload
60
- @payload ||= decode_payload
76
+ raise JWT::DecodeError, 'Verify the token signature before accessing the payload' unless @signature_verified
77
+
78
+ decoded_payload
79
+ end
80
+
81
+ # Returns the payload of the JWT token without requiring the signature to have been verified.
82
+ # @return [Hash] the payload.
83
+ def unverified_payload
84
+ decoded_payload
61
85
  end
62
86
 
63
87
  # Sets or returns the encoded payload of the JWT token.
@@ -72,6 +96,22 @@ module JWT
72
96
  [encoded_header, encoded_payload].join('.')
73
97
  end
74
98
 
99
+ # Verifies the token signature and claims.
100
+ # By default it verifies the 'exp' claim.
101
+ #
102
+ # @example
103
+ # encoded_token.verify!(signature: { algorithm: 'HS256', key: 'secret' }, claims: [:exp])
104
+ #
105
+ # @param signature [Hash] the parameters for signature verification (see {#verify_signature!}).
106
+ # @param claims [Array<Symbol>, Hash] the claims to verify (see {#verify_claims!}).
107
+ # @return [nil]
108
+ # @raise [JWT::DecodeError] if the signature or claim verification fails.
109
+ def verify!(signature:, claims: [:exp])
110
+ verify_signature!(**signature)
111
+ claims.is_a?(Array) ? verify_claims!(*claims) : verify_claims!(claims)
112
+ nil
113
+ end
114
+
75
115
  # Verifies the signature of the JWT token.
76
116
  #
77
117
  # @param algorithm [String, Array<String>, Object, Array<Object>] the algorithm(s) to use for verification.
@@ -96,11 +136,34 @@ module JWT
96
136
  # @param key [String, Array<String>] the key(s) to use for verification.
97
137
  # @return [Boolean] true if the signature is valid, false otherwise.
98
138
  def valid_signature?(algorithm:, key:)
99
- Array(JWA.resolve_and_sort(algorithms: algorithm, preferred_algorithm: header['alg'])).any? do |algo|
139
+ valid = Array(JWA.resolve_and_sort(algorithms: algorithm, preferred_algorithm: header['alg'])).any? do |algo|
100
140
  Array(key).any? do |one_key|
101
141
  algo.verify(data: signing_input, signature: signature, verification_key: one_key)
102
142
  end
103
143
  end
144
+
145
+ valid.tap { |verified| @signature_verified = verified }
146
+ end
147
+
148
+ # Verifies the claims of the token.
149
+ # @param options [Array<Symbol>, Hash] the claims to verify.
150
+ # @raise [JWT::DecodeError] if the claims are invalid.
151
+ def verify_claims!(*options)
152
+ Claims::Verifier.verify!(ClaimsContext.new(self), *options)
153
+ end
154
+
155
+ # Returns the errors of the claims of the token.
156
+ # @param options [Array<Symbol>, Hash] the claims to verify.
157
+ # @return [Array<Symbol>] the errors of the claims.
158
+ def claim_errors(*options)
159
+ Claims::Verifier.errors(ClaimsContext.new(self), *options)
160
+ end
161
+
162
+ # Returns whether the claims of the token are valid.
163
+ # @param options [Array<Symbol>, Hash] the claims to verify.
164
+ # @return [Boolean] whether the claims are valid.
165
+ def valid_claims?(*options)
166
+ claim_errors(*options).empty?
104
167
  end
105
168
 
106
169
  alias to_s jwt
@@ -135,5 +198,9 @@ module JWT
135
198
  rescue ::JSON::ParserError
136
199
  raise JWT::DecodeError, 'Invalid segment encoding'
137
200
  end
201
+
202
+ def decoded_payload
203
+ @decoded_payload ||= decode_payload
204
+ end
138
205
  end
139
206
  end
data/lib/jwt/jwa/ecdsa.rb CHANGED
@@ -56,10 +56,6 @@ module JWT
56
56
  register_algorithm(new(v[:algorithm], v[:digest]))
57
57
  end
58
58
 
59
- def self.from_algorithm(algorithm)
60
- new(algorithm, algorithm.downcase.gsub('es', 'sha'))
61
- end
62
-
63
59
  def self.curve_by_name(name)
64
60
  NAMED_CURVES.fetch(name) do
65
61
  raise UnsupportedEcdsaCurve, "The ECDSA curve '#{name}' is not supported"
data/lib/jwt/jwa/hmac.rb CHANGED
@@ -6,10 +6,6 @@ module JWT
6
6
  class Hmac
7
7
  include JWT::JWA::SigningAlgorithm
8
8
 
9
- def self.from_algorithm(algorithm)
10
- new(algorithm, OpenSSL::Digest.new(algorithm.downcase.gsub('hs', 'sha')))
11
- end
12
-
13
9
  def initialize(alg, digest)
14
10
  @alg = alg
15
11
  @digest = digest
data/lib/jwt/jwa/ps.rb CHANGED
@@ -13,6 +13,7 @@ module JWT
13
13
 
14
14
  def sign(data:, signing_key:)
15
15
  raise_sign_error!("The given key is a #{signing_key.class}. It has to be an OpenSSL::PKey::RSA instance.") unless signing_key.is_a?(::OpenSSL::PKey::RSA)
16
+ raise_sign_error!('The key length must be greater than or equal to 2048 bits') if signing_key.n.num_bits < 2048
16
17
 
17
18
  signing_key.sign_pss(digest_algorithm, data, salt_length: :digest, mgf1_hash: digest_algorithm)
18
19
  end
data/lib/jwt/jwa/rsa.rb CHANGED
@@ -13,6 +13,7 @@ module JWT
13
13
 
14
14
  def sign(data:, signing_key:)
15
15
  raise_sign_error!("The given key is a #{signing_key.class}. It has to be an OpenSSL::PKey::RSA instance") unless signing_key.is_a?(OpenSSL::PKey::RSA)
16
+ raise_sign_error!('The key length must be greater than or equal to 2048 bits') if signing_key.n.num_bits < 2048
16
17
 
17
18
  signing_key.sign(digest, data)
18
19
  end
@@ -14,7 +14,6 @@ module JWT
14
14
 
15
15
  def self.included(klass)
16
16
  klass.extend(ClassMethods)
17
- klass.include(JWT::JWA::Compat)
18
17
  end
19
18
 
20
19
  attr_reader :alg
data/lib/jwt/jwa.rb CHANGED
@@ -2,13 +2,6 @@
2
2
 
3
3
  require 'openssl'
4
4
 
5
- begin
6
- require 'rbnacl'
7
- rescue LoadError
8
- raise if defined?(RbNaCl)
9
- end
10
-
11
- require_relative 'jwa/compat'
12
5
  require_relative 'jwa/signing_algorithm'
13
6
  require_relative 'jwa/ecdsa'
14
7
  require_relative 'jwa/hmac'
@@ -16,15 +9,6 @@ require_relative 'jwa/none'
16
9
  require_relative 'jwa/ps'
17
10
  require_relative 'jwa/rsa'
18
11
  require_relative 'jwa/unsupported'
19
- require_relative 'jwa/wrapper'
20
-
21
- require_relative 'jwa/eddsa' if JWT.rbnacl?
22
-
23
- if JWT.rbnacl_6_or_greater?
24
- require_relative 'jwa/hmac_rbnacl'
25
- elsif JWT.rbnacl?
26
- require_relative 'jwa/hmac_rbnacl_fixed'
27
- end
28
12
 
29
13
  module JWT
30
14
  # The JWA module contains all supported algorithms.
@@ -34,10 +18,7 @@ module JWT
34
18
  def resolve(algorithm)
35
19
  return find(algorithm) if algorithm.is_a?(String) || algorithm.is_a?(Symbol)
36
20
 
37
- unless algorithm.is_a?(SigningAlgorithm)
38
- Deprecations.warning('Custom algorithms are required to include JWT::JWA::SigningAlgorithm. Custom algorithms that do not include this module may stop working in the next major version of ruby-jwt.')
39
- return Wrapper.new(algorithm)
40
- end
21
+ raise ArgumentError, 'Custom algorithms are required to include JWT::JWA::SigningAlgorithm' unless algorithm.is_a?(SigningAlgorithm)
41
22
 
42
23
  algorithm
43
24
  end
@@ -47,12 +28,6 @@ module JWT
47
28
  algs = Array(algorithms).map { |alg| JWA.resolve(alg) }
48
29
  algs.partition { |alg| alg.valid_alg?(preferred_algorithm) }.flatten
49
30
  end
50
-
51
- # @deprecated The `::JWT::JWA.create` method is deprecated and will be removed in the next major version of ruby-jwt.
52
- def create(algorithm)
53
- Deprecations.warning('The ::JWT::JWA.create method is deprecated and will be removed in the next major version of ruby-jwt.')
54
- resolve(algorithm)
55
- end
56
31
  end
57
32
  end
58
33
  end
data/lib/jwt/jwk/ec.rb CHANGED
@@ -124,10 +124,6 @@ module JWT
124
124
  ::JWT::Base64.url_encode(octets)
125
125
  end
126
126
 
127
- def encode_open_ssl_bn(key_part)
128
- ::JWT::Base64.url_encode(key_part.to_s(BINARY))
129
- end
130
-
131
127
  def parse_ec_key(key)
132
128
  crv, x_octets, y_octets = keypair_components(key)
133
129
  octets = key.private_key&.to_bn&.to_s(BINARY)
data/lib/jwt/jwk/hmac.rb CHANGED
@@ -70,7 +70,7 @@ module JWT
70
70
  private
71
71
 
72
72
  def secret
73
- self[:k]
73
+ @secret ||= ::JWT::Base64.url_decode(self[:k])
74
74
  end
75
75
 
76
76
  def extract_key_params(key)
@@ -78,7 +78,7 @@ module JWT
78
78
  when JWT::JWK::HMAC
79
79
  key.export(include_private: true)
80
80
  when String # Accept String key as input
81
- { kty: KTY, k: key }
81
+ { kty: KTY, k: ::JWT::Base64.url_encode(key) }
82
82
  when Hash
83
83
  key.transform_keys(&:to_sym)
84
84
  else