jwt 3.1.2 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4fbf6e518cee3ac505360ea356f34fab6a68c5dfc2112671105085fbb03c08df
4
- data.tar.gz: 0f206bdf51b4a979b6f734d6582f7e18762f2d3a1ee7feb38499fc4a92d77115
3
+ metadata.gz: 0bb396cd444a952a2c732720675f053ce5c4557978acf54acc0753a98b81f402
4
+ data.tar.gz: deeb4ea2635a1620f8dab94b4b7b2d6b7a01087ea0f45b9f79b408c9a7b23c25
5
5
  SHA512:
6
- metadata.gz: 4fa9df3dae62f1abbe065fd144641a8869faddd45be94cb58871dc690fead80f38f741b5de5319b5a31d8d28fe16ae32627f27d396cbe2f91f80acc9a6d3e477
7
- data.tar.gz: 41e30090c5ee55b3706b4d2bddd73dc596e4db46669c292af56bca9ebe9f36a8eafe7ce33578f3012d87f910a6880ee26e6c26c46bfda59470a23f24e47a739d
6
+ metadata.gz: d9913bf66785a88c127148c5a4a3fd6dcd9a28820f53c20635ab5469cebdfb159366566ba8b198154c9a541927224c11e75ced5c9f5b69c1d32a9e01764ca0dd
7
+ data.tar.gz: e2d4765fa99b67229f3204b917aea480a1d4027c23a216f307f92d6ffa8acd049b72071377d7e0c4c8fcba5eb845f9751c944dbc87e9a597020665859edd40c1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.2.0](https://github.com/jwt/ruby-jwt/tree/v3.2.0) (2026-05-13)
4
+
5
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v3.1.2...v3.2.0)
6
+
7
+ **Features:**
8
+
9
+ - Add `enforce_hmac_key_length` configuration option [#716](https://github.com/jwt/ruby-jwt/pull/716) - ([@304](https://github.com/304))
10
+
11
+ **Fixes and enhancements:**
12
+
13
+ - Reject `nil` and empty HMAC keys when signing and verifying ([CVE-2026-45363](https://www.cve.org/CVERecord?id=CVE-2026-45363) / [GHSA-c32j-vqhx-rx3x](https://github.com/jwt/ruby-jwt/security/advisories/GHSA-c32j-vqhx-rx3x))
14
+ - Fix compatibility with the openssl 4.0 gem [#706](https://github.com/jwt/ruby-jwt/pull/706)
15
+ - Test against Ruby 4.0 on CI [#707](https://github.com/jwt/ruby-jwt/pull/707)
16
+ - Fix type error when header is not a JSON object [#715](https://github.com/jwt/ruby-jwt/pull/715) - ([@304](https://github.com/304))
17
+
3
18
  ## [v3.1.2](https://github.com/jwt/ruby-jwt/tree/v3.1.2) (2025-06-28)
4
19
 
5
20
  [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v3.1.1...v3.1.2)
@@ -74,7 +89,7 @@ Take a look at the [upgrade guide](UPGRADING.md) for more details.
74
89
  - JWT::Token and JWT::EncodedToken for signing and verifying tokens [#621](https://github.com/jwt/ruby-jwt/pull/621) ([@anakinj](https://github.com/anakinj))
75
90
  - Detached payload support for JWT::Token and JWT::EncodedToken [#630](https://github.com/jwt/ruby-jwt/pull/630) ([@anakinj](https://github.com/anakinj))
76
91
  - Skip decoding payload if b64 header is present and false [#631](https://github.com/jwt/ruby-jwt/pull/631) ([@anakinj](https://github.com/anakinj))
77
- - Remove a few custom Rubocop configs [#638](https://github.com/jwt/ruby-jwt/pull/638) ([@anakinj](https://github.com/anakinj))
92
+ - Remove a few custom RuboCop configs [#638](https://github.com/jwt/ruby-jwt/pull/638) ([@anakinj](https://github.com/anakinj))
78
93
 
79
94
  **Fixes and enhancements:**
80
95
 
@@ -352,7 +367,7 @@ Take a look at the [upgrade guide](UPGRADING.md) for more details.
352
367
  **Implemented enhancements:**
353
368
 
354
369
  - JWK does not decode. [\#332](https://github.com/jwt/ruby-jwt/issues/332)
355
- - Inconsistent use of symbol and string keys in args \(exp and alrogithm\). [\#331](https://github.com/jwt/ruby-jwt/issues/331)
370
+ - Inconsistent use of symbol and string keys in args \(exp and algorithm\). [\#331](https://github.com/jwt/ruby-jwt/issues/331)
356
371
  - Pin simplecov to \< 0.18 [\#356](https://github.com/jwt/ruby-jwt/pull/356) ([anakinj](https://github.com/anakinj))
357
372
  - verifies algorithm before evaluating keyfinder [\#346](https://github.com/jwt/ruby-jwt/pull/346) ([jb08](https://github.com/jb08))
358
373
  - Update Rails 6 appraisal to use actual release version [\#336](https://github.com/jwt/ruby-jwt/pull/336) ([smudge](https://github.com/smudge))
@@ -467,7 +482,7 @@ Take a look at the [upgrade guide](UPGRADING.md) for more details.
467
482
  - 'DecodeError'will replace 'ExpiredSignature' [\#260](https://github.com/jwt/ruby-jwt/issues/260)
468
483
  - TypeError: no implicit conversion of OpenSSL::PKey::RSA into String [\#259](https://github.com/jwt/ruby-jwt/issues/259)
469
484
  - NameError: uninitialized constant JWT::Algos::Eddsa::RbNaCl [\#258](https://github.com/jwt/ruby-jwt/issues/258)
470
- - Get new token if curren token expired [\#256](https://github.com/jwt/ruby-jwt/issues/256)
485
+ - Get new token if current token expired [\#256](https://github.com/jwt/ruby-jwt/issues/256)
471
486
  - Infer algorithm from header [\#254](https://github.com/jwt/ruby-jwt/issues/254)
472
487
  - Why is the result of decode is an array? [\#252](https://github.com/jwt/ruby-jwt/issues/252)
473
488
  - Add support for headless token [\#251](https://github.com/jwt/ruby-jwt/issues/251)
@@ -686,8 +701,8 @@ Take a look at the [upgrade guide](UPGRADING.md) for more details.
686
701
  **Implemented enhancements:**
687
702
 
688
703
  - Refactor obsolete code for ruby 1.8 support [\#120](https://github.com/jwt/ruby-jwt/issues/120)
689
- - Fix "Rubocop/Metrics/CyclomaticComplexity" issue in lib/jwt.rb [\#106](https://github.com/jwt/ruby-jwt/issues/106)
690
- - Fix "Rubocop/Metrics/CyclomaticComplexity" issue in lib/jwt.rb [\#105](https://github.com/jwt/ruby-jwt/issues/105)
704
+ - Fix "RuboCop/Metrics/CyclomaticComplexity" issue in lib/jwt.rb [\#106](https://github.com/jwt/ruby-jwt/issues/106)
705
+ - Fix "RuboCop/Metrics/CyclomaticComplexity" issue in lib/jwt.rb [\#105](https://github.com/jwt/ruby-jwt/issues/105)
691
706
  - Allow a proc to be passed for JTI verification [\#126](https://github.com/jwt/ruby-jwt/pull/126) ([yahooguntu](https://github.com/yahooguntu))
692
707
  - Relax restrictions on "jti" claim verification [\#113](https://github.com/jwt/ruby-jwt/pull/113) ([lwe](https://github.com/lwe))
693
708
 
@@ -833,13 +848,13 @@ Take a look at the [upgrade guide](UPGRADING.md) for more details.
833
848
  - Signature Verification to Return Verification Error rather than decode error [\#57](https://github.com/jwt/ruby-jwt/issues/57)
834
849
  - Incorrect readme for leeway [\#55](https://github.com/jwt/ruby-jwt/issues/55)
835
850
  - What is the reason behind stripping the = in base64 encoding? [\#54](https://github.com/jwt/ruby-jwt/issues/54)
836
- - Preperations for version 2.x [\#50](https://github.com/jwt/ruby-jwt/issues/50)
851
+ - Preparations for version 2.x [\#50](https://github.com/jwt/ruby-jwt/issues/50)
837
852
  - Release a new version [\#47](https://github.com/jwt/ruby-jwt/issues/47)
838
853
  - Catch up for ActiveWhatever 4.1.1 series [\#40](https://github.com/jwt/ruby-jwt/issues/40)
839
854
 
840
855
  **Merged pull requests:**
841
856
 
842
- - raise verification error for signiture verification [\#58](https://github.com/jwt/ruby-jwt/pull/58) ([punkle](https://github.com/punkle))
857
+ - raise verification error for signature verification [\#58](https://github.com/jwt/ruby-jwt/pull/58) ([punkle](https://github.com/punkle))
843
858
  - Added support for not before claim verification [\#56](https://github.com/jwt/ruby-jwt/pull/56) ([punkle](https://github.com/punkle))
844
859
 
845
860
  ## [jwt-1.2.1](https://github.com/jwt/ruby-jwt/tree/jwt-1.2.1) (2015-01-22)
data/CONTRIBUTING.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Forking the project
4
4
 
5
- Fork the project on GitHub and clone your own fork. Instuctions on forking can be found from the [GitHub Docs](https://docs.github.com/en/get-started/quickstart/fork-a-repo)
5
+ Fork the project on GitHub and clone your own fork. Instructions on forking can be found from the [GitHub Docs](https://docs.github.com/en/get-started/quickstart/fork-a-repo)
6
6
 
7
7
  ```bash
8
8
  git clone git@github.com:you/ruby-jwt.git
@@ -28,7 +28,7 @@ Before you start with your implementation make sure you are able to get a succes
28
28
 
29
29
  The tests are written with rspec and [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
30
30
 
31
- [Rubocop](https://github.com/rubocop/rubocop) is used to enforce the Ruby style.
31
+ [RuboCop](https://github.com/rubocop/rubocop) is used to enforce the Ruby style.
32
32
 
33
33
  To run the complete set of tests and linter run the following
34
34
 
@@ -24,6 +24,8 @@ module JWT
24
24
  # @return [Array<String>] the list of acceptable algorithms.
25
25
  # @!attribute [rw] required_claims
26
26
  # @return [Array<String>] the list of required claims.
27
+ # @!attribute [rw] enforce_hmac_key_length
28
+ # @return [Boolean] whether to enforce minimum HMAC key lengths. false disables validation (default).
27
29
 
28
30
  attr_accessor :verify_expiration,
29
31
  :verify_not_before,
@@ -34,7 +36,8 @@ module JWT
34
36
  :verify_sub,
35
37
  :leeway,
36
38
  :algorithms,
37
- :required_claims
39
+ :required_claims,
40
+ :enforce_hmac_key_length
38
41
 
39
42
  # Initializes a new DecodeConfiguration instance with default settings.
40
43
  def initialize
@@ -48,6 +51,7 @@ module JWT
48
51
  @leeway = 0
49
52
  @algorithms = ['HS256']
50
53
  @required_claims = []
54
+ @enforce_hmac_key_length = false
51
55
  end
52
56
 
53
57
  # @api private
@@ -62,7 +66,8 @@ module JWT
62
66
  verify_sub: verify_sub,
63
67
  leeway: leeway,
64
68
  algorithms: algorithms,
65
- required_claims: required_claims
69
+ required_claims: required_claims,
70
+ enforce_hmac_key_length: enforce_hmac_key_length
66
71
  }
67
72
  end
68
73
  end
data/lib/jwt/decode.rb CHANGED
@@ -58,7 +58,7 @@ module JWT
58
58
 
59
59
  def verify_algo
60
60
  raise JWT::IncorrectAlgorithm, 'An algorithm must be specified' if allowed_algorithms.empty?
61
- raise JWT::DecodeError, 'Token header not a JSON object' unless token.header.is_a?(Hash)
61
+ raise JWT::DecodeError, 'Token header not a JSON object' unless valid_token_header?
62
62
  raise JWT::IncorrectAlgorithm, 'Token is missing alg header' unless alg_in_header
63
63
  raise JWT::IncorrectAlgorithm, 'Expected a different algorithm' if allowed_and_valid_algorithms.empty?
64
64
  end
@@ -113,9 +113,15 @@ module JWT
113
113
  end
114
114
 
115
115
  def none_algorithm?
116
+ return false unless valid_token_header?
117
+
116
118
  alg_in_header == 'none'
117
119
  end
118
120
 
121
+ def valid_token_header?
122
+ token.header.is_a?(Hash)
123
+ end
124
+
119
125
  def alg_in_header
120
126
  token.header['alg']
121
127
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module JWT
6
+ # @private
7
+ class EncodedToken
8
+ # Allow access to the unverified payload for claim verification.
9
+ class ClaimsContext
10
+ extend Forwardable
11
+
12
+ def_delegators :@token, :header, :unverified_payload
13
+
14
+ def initialize(token)
15
+ @token = token
16
+ end
17
+
18
+ def payload
19
+ unverified_payload
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'encoded_token/claims_context'
4
+
3
5
  module JWT
4
6
  # Represents an encoded JWT token
5
7
  #
@@ -12,22 +14,6 @@ module JWT
12
14
  # encoded_token.verify_signature!(algorithm: 'HS256', key: 'secret')
13
15
  # encoded_token.payload # => {'pay' => 'load'}
14
16
  class EncodedToken
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
30
-
31
17
  DEFAULT_CLAIMS = [:exp].freeze
32
18
 
33
19
  private_constant(:DEFAULT_CLAIMS)
data/lib/jwt/jwa/hmac.rb CHANGED
@@ -6,24 +6,32 @@ module JWT
6
6
  class Hmac
7
7
  include JWT::JWA::SigningAlgorithm
8
8
 
9
+ # Minimum key lengths for HMAC algorithms based on RFC 7518 Section 3.2.
10
+ # Keys must be at least the size of the hash output to ensure sufficient
11
+ # entropy for the algorithm's security level.
12
+ MIN_KEY_LENGTHS = {
13
+ 'HS256' => 32,
14
+ 'HS384' => 48,
15
+ 'HS512' => 64
16
+ }.freeze
17
+
9
18
  def initialize(alg, digest)
10
19
  @alg = alg
11
20
  @digest = digest
12
21
  end
13
22
 
14
23
  def sign(data:, signing_key:)
15
- signing_key ||= ''
16
- raise_verify_error!('HMAC key expected to be a String') unless signing_key.is_a?(String)
24
+ ensure_valid_key!(signing_key)
25
+ validate_key_length!(signing_key)
17
26
 
18
27
  OpenSSL::HMAC.digest(digest.new, signing_key, data)
19
- rescue OpenSSL::HMACError => e
20
- raise_verify_error!('OpenSSL 3.0 does not support nil or empty hmac_secret') if signing_key == '' && e.message == 'EVP_PKEY_new_mac_key: malloc failure'
21
-
22
- raise e
23
28
  end
24
29
 
25
30
  def verify(data:, signature:, verification_key:)
26
- SecurityUtils.secure_compare(signature, sign(data: data, signing_key: verification_key))
31
+ ensure_valid_key!(verification_key)
32
+ validate_key_length!(verification_key)
33
+
34
+ SecurityUtils.secure_compare(signature, OpenSSL::HMAC.digest(digest.new, verification_key, data))
27
35
  end
28
36
 
29
37
  register_algorithm(new('HS256', OpenSSL::Digest::SHA256))
@@ -34,6 +42,20 @@ module JWT
34
42
 
35
43
  attr_reader :digest
36
44
 
45
+ def ensure_valid_key!(key)
46
+ raise_verify_error!('HMAC key expected to be a String') unless key.is_a?(String)
47
+ raise_verify_error!('HMAC key cannot be empty') if key.empty?
48
+ end
49
+
50
+ def validate_key_length!(key)
51
+ return unless JWT.configuration.decode.enforce_hmac_key_length
52
+
53
+ min_length = MIN_KEY_LENGTHS[alg]
54
+ return if key.bytesize >= min_length
55
+
56
+ raise_verify_error!("HMAC key must be at least #{min_length} bytes for #{alg} algorithm")
57
+ end
58
+
37
59
  # Copy of https://github.com/rails/rails/blob/v7.0.3.1/activesupport/lib/active_support/security_utils.rb
38
60
  # rubocop:disable Naming/MethodParameterName, Style/StringLiterals, Style/NumericPredicate
39
61
  module SecurityUtils
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module JWA
5
+ # @api private
6
+ class SignerContext
7
+ attr_reader :jwa
8
+
9
+ def initialize(jwa:, key:)
10
+ @jwa = jwa
11
+ @key = key
12
+ end
13
+
14
+ def sign(*args, **kwargs)
15
+ @jwa.sign(*args, **kwargs, signing_key: @key)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module JWA
5
+ # @api private
6
+ class VerifierContext
7
+ attr_reader :jwa
8
+
9
+ def initialize(jwa:, keys:)
10
+ @jwa = jwa
11
+ @keys = Array(keys)
12
+ end
13
+
14
+ def verify(*args, **kwargs)
15
+ @keys.any? do |key|
16
+ @jwa.verify(*args, **kwargs, verification_key: key)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
data/lib/jwt/jwa.rb CHANGED
@@ -9,40 +9,12 @@ require_relative 'jwa/none'
9
9
  require_relative 'jwa/ps'
10
10
  require_relative 'jwa/rsa'
11
11
  require_relative 'jwa/unsupported'
12
+ require_relative 'jwa/verifier_context'
13
+ require_relative 'jwa/signer_context'
12
14
 
13
15
  module JWT
14
16
  # The JWA module contains all supported algorithms.
15
17
  module JWA
16
- # @api private
17
- class VerifierContext
18
- attr_reader :jwa
19
-
20
- def initialize(jwa:, keys:)
21
- @jwa = jwa
22
- @keys = Array(keys)
23
- end
24
-
25
- def verify(*args, **kwargs)
26
- @keys.any? do |key|
27
- @jwa.verify(*args, **kwargs, verification_key: key)
28
- end
29
- end
30
- end
31
-
32
- # @api private
33
- class SignerContext
34
- attr_reader :jwa
35
-
36
- def initialize(jwa:, key:)
37
- @jwa = jwa
38
- @key = key
39
- end
40
-
41
- def sign(*args, **kwargs)
42
- @jwa.sign(*args, **kwargs, signing_key: @key)
43
- end
44
- end
45
-
46
18
  class << self
47
19
  # @api private
48
20
  def resolve(algorithm)
data/lib/jwt/jwk/rsa.rb CHANGED
@@ -195,7 +195,7 @@ module JWT
195
195
 
196
196
  if ::JWT.openssl_3?
197
197
  alias create_rsa_key create_rsa_key_using_der
198
- elsif OpenSSL::PKey::RSA.new.respond_to?(:set_key)
198
+ elsif OpenSSL::PKey::RSA.method_defined?(:set_key)
199
199
  alias create_rsa_key create_rsa_key_using_sets
200
200
  else
201
201
  alias create_rsa_key create_rsa_key_using_accessors
data/lib/jwt/version.rb CHANGED
@@ -15,8 +15,8 @@ module JWT
15
15
  # Version constants
16
16
  module VERSION
17
17
  MAJOR = 3
18
- MINOR = 1
19
- TINY = 2
18
+ MINOR = 2
19
+ TINY = 0
20
20
  PRE = nil
21
21
 
22
22
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
@@ -32,14 +32,6 @@ module JWT
32
32
  true if 3 * 0x10000000 <= OpenSSL::OPENSSL_VERSION_NUMBER
33
33
  end
34
34
 
35
- # Checks if there is an OpenSSL 3 HMAC empty key regression.
36
- #
37
- # @return [Boolean] true if there is an OpenSSL 3 HMAC empty key regression, false otherwise.
38
- # @api private
39
- def self.openssl_3_hmac_empty_key_regression?
40
- openssl_3? && openssl_version <= ::Gem::Version.new('3.0.0')
41
- end
42
-
43
35
  # Returns the OpenSSL version.
44
36
  #
45
37
  # @return [Gem::Version] the OpenSSL version.
@@ -40,7 +40,7 @@ module JWT
40
40
  end
41
41
 
42
42
  def parse_certificates(x5c_header_or_certificates)
43
- if x5c_header_or_certificates.all? { |obj| obj.is_a?(OpenSSL::X509::Certificate) }
43
+ if x5c_header_or_certificates.all?(OpenSSL::X509::Certificate)
44
44
  x5c_header_or_certificates
45
45
  else
46
46
  x5c_header_or_certificates.map do |encoded|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jwt
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.2
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Rudat
@@ -171,6 +171,7 @@ files:
171
171
  - lib/jwt/decode.rb
172
172
  - lib/jwt/encode.rb
173
173
  - lib/jwt/encoded_token.rb
174
+ - lib/jwt/encoded_token/claims_context.rb
174
175
  - lib/jwt/error.rb
175
176
  - lib/jwt/json.rb
176
177
  - lib/jwt/jwa.rb
@@ -179,8 +180,10 @@ files:
179
180
  - lib/jwt/jwa/none.rb
180
181
  - lib/jwt/jwa/ps.rb
181
182
  - lib/jwt/jwa/rsa.rb
183
+ - lib/jwt/jwa/signer_context.rb
182
184
  - lib/jwt/jwa/signing_algorithm.rb
183
185
  - lib/jwt/jwa/unsupported.rb
186
+ - lib/jwt/jwa/verifier_context.rb
184
187
  - lib/jwt/jwk.rb
185
188
  - lib/jwt/jwk/ec.rb
186
189
  - lib/jwt/jwk/hmac.rb
@@ -199,7 +202,7 @@ licenses:
199
202
  - MIT
200
203
  metadata:
201
204
  bug_tracker_uri: https://github.com/jwt/ruby-jwt/issues
202
- changelog_uri: https://github.com/jwt/ruby-jwt/blob/v3.1.2/CHANGELOG.md
205
+ changelog_uri: https://github.com/jwt/ruby-jwt/blob/v3.2.0/CHANGELOG.md
203
206
  rubygems_mfa_required: 'true'
204
207
  rdoc_options: []
205
208
  require_paths:
@@ -215,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
215
218
  - !ruby/object:Gem::Version
216
219
  version: '0'
217
220
  requirements: []
218
- rubygems_version: 3.6.7
221
+ rubygems_version: 4.0.10
219
222
  specification_version: 4
220
223
  summary: JSON Web Token implementation in Ruby
221
224
  test_files: []