jwt 2.10.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +104 -41
  3. data/CODE_OF_CONDUCT.md +14 -14
  4. data/CONTRIBUTING.md +11 -12
  5. data/README.md +190 -221
  6. data/UPGRADING.md +47 -0
  7. data/lib/jwt/base64.rb +1 -10
  8. data/lib/jwt/claims/numeric.rb +0 -32
  9. data/lib/jwt/claims.rb +0 -7
  10. data/lib/jwt/configuration/container.rb +0 -1
  11. data/lib/jwt/configuration/decode_configuration.rb +7 -2
  12. data/lib/jwt/decode.rb +25 -16
  13. data/lib/jwt/encoded_token/claims_context.rb +23 -0
  14. data/lib/jwt/encoded_token.rb +97 -14
  15. data/lib/jwt/error.rb +0 -3
  16. data/lib/jwt/jwa/ecdsa.rb +25 -4
  17. data/lib/jwt/jwa/hmac.rb +28 -10
  18. data/lib/jwt/jwa/ps.rb +1 -0
  19. data/lib/jwt/jwa/rsa.rb +1 -0
  20. data/lib/jwt/jwa/signer_context.rb +19 -0
  21. data/lib/jwt/jwa/signing_algorithm.rb +0 -1
  22. data/lib/jwt/jwa/verifier_context.rb +21 -0
  23. data/lib/jwt/jwa.rb +43 -26
  24. data/lib/jwt/jwk/ec.rb +52 -62
  25. data/lib/jwt/jwk/hmac.rb +3 -3
  26. data/lib/jwt/jwk/key_base.rb +15 -1
  27. data/lib/jwt/jwk/key_finder.rb +35 -9
  28. data/lib/jwt/jwk/rsa.rb +6 -2
  29. data/lib/jwt/jwk.rb +0 -1
  30. data/lib/jwt/token.rb +26 -7
  31. data/lib/jwt/version.rb +4 -28
  32. data/lib/jwt/x5c_key_finder.rb +1 -1
  33. data/lib/jwt.rb +1 -7
  34. data/ruby-jwt.gemspec +1 -0
  35. metadata +21 -13
  36. data/lib/jwt/claims/verification_methods.rb +0 -20
  37. data/lib/jwt/claims_validator.rb +0 -18
  38. data/lib/jwt/deprecations.rb +0 -49
  39. data/lib/jwt/jwa/compat.rb +0 -32
  40. data/lib/jwt/jwa/eddsa.rb +0 -35
  41. data/lib/jwt/jwa/hmac_rbnacl.rb +0 -50
  42. data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +0 -47
  43. data/lib/jwt/jwa/wrapper.rb +0 -44
  44. data/lib/jwt/jwk/okp_rbnacl.rb +0 -109
  45. data/lib/jwt/verify.rb +0 -40
@@ -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
@@ -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,8 @@ 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
12
+ require_relative 'jwa/verifier_context'
13
+ require_relative 'jwa/signer_context'
28
14
 
29
15
  module JWT
30
16
  # The JWA module contains all supported algorithms.
@@ -34,24 +20,55 @@ module JWT
34
20
  def resolve(algorithm)
35
21
  return find(algorithm) if algorithm.is_a?(String) || algorithm.is_a?(Symbol)
36
22
 
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
23
+ raise ArgumentError, 'Algorithm must be provided' if algorithm.nil?
24
+
25
+ raise ArgumentError, 'Custom algorithms are required to include JWT::JWA::SigningAlgorithm' unless algorithm.is_a?(SigningAlgorithm)
41
26
 
42
27
  algorithm
43
28
  end
44
29
 
45
30
  # @api private
46
31
  def resolve_and_sort(algorithms:, preferred_algorithm:)
47
- algs = Array(algorithms).map { |alg| JWA.resolve(alg) }
48
- algs.partition { |alg| alg.valid_alg?(preferred_algorithm) }.flatten
32
+ Array(algorithms).map { |alg| JWA.resolve(alg) }
33
+ .partition { |alg| alg.valid_alg?(preferred_algorithm) }
34
+ .flatten
49
35
  end
50
36
 
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)
37
+ # @api private
38
+ def create_signer(algorithm:, key:)
39
+ if key.is_a?(JWK::KeyBase)
40
+ validate_jwk_algorithms!(key, algorithm, DecodeError)
41
+
42
+ return key
43
+ end
44
+
45
+ SignerContext.new(jwa: resolve(algorithm), key: key)
46
+ end
47
+
48
+ # @api private
49
+ def create_verifiers(algorithms:, keys:, preferred_algorithm:)
50
+ jwks, other_keys = keys.partition { |key| key.is_a?(JWK::KeyBase) }
51
+
52
+ validate_jwk_algorithms!(jwks, algorithms, VerificationError)
53
+
54
+ jwks + resolve_and_sort(algorithms: algorithms,
55
+ preferred_algorithm: preferred_algorithm)
56
+ .map { |jwa| VerifierContext.new(jwa: jwa, keys: other_keys) }
57
+ end
58
+
59
+ # @api private
60
+ def validate_jwk_algorithms!(jwks, algorithms, error_class)
61
+ algorithms = Array(algorithms)
62
+
63
+ return if algorithms.empty?
64
+
65
+ return if Array(jwks).all? do |jwk|
66
+ algorithms.any? do |alg|
67
+ jwk.jwa.valid_alg?(alg)
68
+ end
69
+ end
70
+
71
+ raise error_class, "Provided JWKs do not support one of the specified algorithms: #{algorithms.join(', ')}"
55
72
  end
56
73
  end
57
74
  end
data/lib/jwt/jwk/ec.rb CHANGED
@@ -68,7 +68,14 @@ module JWT
68
68
  def []=(key, value)
69
69
  raise ArgumentError, 'cannot overwrite cryptographic key attributes' if EC_KEY_ELEMENTS.include?(key.to_sym)
70
70
 
71
- super(key, value)
71
+ super
72
+ end
73
+
74
+ def jwa
75
+ return super if self[:alg]
76
+
77
+ curve_name = self.class.to_openssl_curve(self[:crv])
78
+ JWA.resolve(JWA::Ecdsa.curve_by_name(curve_name)[:algorithm])
72
79
  end
73
80
 
74
81
  private
@@ -124,10 +131,6 @@ module JWT
124
131
  ::JWT::Base64.url_encode(octets)
125
132
  end
126
133
 
127
- def encode_open_ssl_bn(key_part)
128
- ::JWT::Base64.url_encode(key_part.to_s(BINARY))
129
- end
130
-
131
134
  def parse_ec_key(key)
132
135
  crv, x_octets, y_octets = keypair_components(key)
133
136
  octets = key.private_key&.to_bn&.to_s(BINARY)
@@ -140,67 +143,54 @@ module JWT
140
143
  }.compact
141
144
  end
142
145
 
143
- if ::JWT.openssl_3?
144
- def create_ec_key(jwk_crv, jwk_x, jwk_y, jwk_d) # rubocop:disable Metrics/MethodLength
145
- curve = EC.to_openssl_curve(jwk_crv)
146
- x_octets = decode_octets(jwk_x)
147
- y_octets = decode_octets(jwk_y)
148
-
149
- point = OpenSSL::PKey::EC::Point.new(
150
- OpenSSL::PKey::EC::Group.new(curve),
151
- OpenSSL::BN.new([0x04, x_octets, y_octets].pack('Ca*a*'), 2)
152
- )
153
-
154
- sequence = if jwk_d
155
- # https://datatracker.ietf.org/doc/html/rfc5915.html
156
- # ECPrivateKey ::= SEQUENCE {
157
- # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
158
- # privateKey OCTET STRING,
159
- # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
160
- # publicKey [1] BIT STRING OPTIONAL
161
- # }
162
-
163
- OpenSSL::ASN1::Sequence([
164
- OpenSSL::ASN1::Integer(1),
165
- OpenSSL::ASN1::OctetString(OpenSSL::BN.new(decode_octets(jwk_d), 2).to_s(2)),
166
- OpenSSL::ASN1::ObjectId(curve, 0, :EXPLICIT),
167
- OpenSSL::ASN1::BitString(point.to_octet_string(:uncompressed), 1, :EXPLICIT)
168
- ])
169
- else
170
- OpenSSL::ASN1::Sequence([
171
- OpenSSL::ASN1::Sequence([OpenSSL::ASN1::ObjectId('id-ecPublicKey'), OpenSSL::ASN1::ObjectId(curve)]),
172
- OpenSSL::ASN1::BitString(point.to_octet_string(:uncompressed))
173
- ])
174
- end
146
+ def create_point(jwk_crv, jwk_x, jwk_y)
147
+ curve = EC.to_openssl_curve(jwk_crv)
148
+ x_octets = decode_octets(jwk_x)
149
+ y_octets = decode_octets(jwk_y)
175
150
 
151
+ # The details of the `Point` instantiation are covered in:
152
+ # - https://docs.ruby-lang.org/en/2.4.0/OpenSSL/PKey/EC.html
153
+ # - https://www.openssl.org/docs/manmaster/man3/EC_POINT_new.html
154
+ # - https://tools.ietf.org/html/rfc5480#section-2.2
155
+ # - https://www.secg.org/SEC1-Ver-1.0.pdf
156
+ # Section 2.3.3 of the last of these references specifies that the
157
+ # encoding of an uncompressed point consists of the byte `0x04` followed
158
+ # by the x value then the y value.
159
+ OpenSSL::PKey::EC::Point.new(
160
+ OpenSSL::PKey::EC::Group.new(curve),
161
+ OpenSSL::BN.new([0x04, x_octets, y_octets].pack('Ca*a*'), 2)
162
+ )
163
+ end
164
+
165
+ if ::JWT.openssl_3?
166
+ def create_ec_key(jwk_crv, jwk_x, jwk_y, jwk_d)
167
+ point = create_point(jwk_crv, jwk_x, jwk_y)
168
+
169
+ return ::JWT::JWA::Ecdsa.create_public_key_from_point(point) unless jwk_d
170
+
171
+ # https://datatracker.ietf.org/doc/html/rfc5915.html
172
+ # ECPrivateKey ::= SEQUENCE {
173
+ # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
174
+ # privateKey OCTET STRING,
175
+ # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
176
+ # publicKey [1] BIT STRING OPTIONAL
177
+ # }
178
+
179
+ sequence = OpenSSL::ASN1::Sequence([
180
+ OpenSSL::ASN1::Integer(1),
181
+ OpenSSL::ASN1::OctetString(OpenSSL::BN.new(decode_octets(jwk_d), 2).to_s(2)),
182
+ OpenSSL::ASN1::ObjectId(point.group.curve_name, 0, :EXPLICIT),
183
+ OpenSSL::ASN1::BitString(point.to_octet_string(:uncompressed), 1, :EXPLICIT)
184
+ ])
176
185
  OpenSSL::PKey::EC.new(sequence.to_der)
177
186
  end
178
187
  else
179
188
  def create_ec_key(jwk_crv, jwk_x, jwk_y, jwk_d)
180
- curve = EC.to_openssl_curve(jwk_crv)
181
-
182
- x_octets = decode_octets(jwk_x)
183
- y_octets = decode_octets(jwk_y)
184
-
185
- key = OpenSSL::PKey::EC.new(curve)
186
-
187
- # The details of the `Point` instantiation are covered in:
188
- # - https://docs.ruby-lang.org/en/2.4.0/OpenSSL/PKey/EC.html
189
- # - https://www.openssl.org/docs/manmaster/man3/EC_POINT_new.html
190
- # - https://tools.ietf.org/html/rfc5480#section-2.2
191
- # - https://www.secg.org/SEC1-Ver-1.0.pdf
192
- # Section 2.3.3 of the last of these references specifies that the
193
- # encoding of an uncompressed point consists of the byte `0x04` followed
194
- # by the x value then the y value.
195
- point = OpenSSL::PKey::EC::Point.new(
196
- OpenSSL::PKey::EC::Group.new(curve),
197
- OpenSSL::BN.new([0x04, x_octets, y_octets].pack('Ca*a*'), 2)
198
- )
199
-
200
- key.public_key = point
201
- key.private_key = OpenSSL::BN.new(decode_octets(jwk_d), 2) if jwk_d
202
-
203
- key
189
+ point = create_point(jwk_crv, jwk_x, jwk_y)
190
+
191
+ ::JWT::JWA::Ecdsa.create_public_key_from_point(point).tap do |key|
192
+ key.private_key = OpenSSL::BN.new(decode_octets(jwk_d), 2) if jwk_d
193
+ end
204
194
  end
205
195
  end
206
196
 
@@ -209,7 +199,7 @@ module JWT
209
199
  # Some base64 encoders on some platform omit a single 0-byte at
210
200
  # the start of either Y or X coordinate of the elliptic curve point.
211
201
  # This leads to an encoding error when data is passed to OpenSSL BN.
212
- # It is know to have happend to exported JWKs on a Java application and
202
+ # It is know to have happened to exported JWKs on a Java application and
213
203
  # on a Flutter/Dart application (both iOS and Android). All that is
214
204
  # needed to fix the problem is adding a leading 0-byte. We know the
215
205
  # required byte is 0 because with any other byte the point is no longer
data/lib/jwt/jwk/hmac.rb CHANGED
@@ -64,13 +64,13 @@ module JWT
64
64
  def []=(key, value)
65
65
  raise ArgumentError, 'cannot overwrite cryptographic key attributes' if HMAC_KEY_ELEMENTS.include?(key.to_sym)
66
66
 
67
- super(key, value)
67
+ super
68
68
  end
69
69
 
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
@@ -42,6 +42,14 @@ module JWT
42
42
  other.is_a?(::JWT::JWK::KeyBase) && self[:kid] == other[:kid]
43
43
  end
44
44
 
45
+ def verify(**kwargs)
46
+ jwa.verify(**kwargs, verification_key: verify_key)
47
+ end
48
+
49
+ def sign(**kwargs)
50
+ jwa.sign(**kwargs, signing_key: signing_key)
51
+ end
52
+
45
53
  alias eql? ==
46
54
 
47
55
  def <=>(other)
@@ -50,7 +58,13 @@ module JWT
50
58
  self[:kid] <=> other[:kid]
51
59
  end
52
60
 
53
- private
61
+ def jwa
62
+ raise JWT::JWKError, 'Could not resolve the JWA, the "alg" parameter is missing' unless self[:alg]
63
+
64
+ JWA.resolve(self[:alg]).tap do |jwa|
65
+ raise JWT::JWKError, 'none algorithm usage not supported via JWK' if jwa.is_a?(JWA::None)
66
+ end
67
+ end
54
68
 
55
69
  attr_reader :parameters
56
70
  end
@@ -2,8 +2,16 @@
2
2
 
3
3
  module JWT
4
4
  module JWK
5
- # @api private
5
+ # JSON Web Key keyfinder
6
+ # To find the key for a given kid
6
7
  class KeyFinder
8
+ # Initializes a new KeyFinder instance.
9
+ # @param [Hash] options the options to create a KeyFinder with
10
+ # @option options [Proc, JWT::JWK::Set] :jwks the jwks or a loader proc
11
+ # @option options [Boolean] :allow_nil_kid whether to allow nil kid
12
+ # @option options [Array] :key_fields the fields to use for key matching,
13
+ # the order of the fields are used to determine
14
+ # the priority of the keys.
7
15
  def initialize(options)
8
16
  @allow_nil_kid = options[:allow_nil_kid]
9
17
  jwks_or_loader = options[:jwks]
@@ -13,13 +21,16 @@ module JWT
13
21
  else
14
22
  ->(_options) { jwks_or_loader }
15
23
  end
24
+
25
+ @key_fields = options[:key_fields] || %i[kid]
16
26
  end
17
27
 
18
- def key_for(kid)
19
- raise ::JWT::DecodeError, 'No key id (kid) found from token headers' unless kid || @allow_nil_kid
20
- raise ::JWT::DecodeError, 'Invalid type for kid header parameter' unless kid.nil? || kid.is_a?(String)
28
+ # Returns the verification key for the given kid
29
+ # @param [String] kid the key id
30
+ def key_for(kid, key_field = :kid)
31
+ raise ::JWT::DecodeError, "Invalid type for #{key_field} header parameter" unless kid.nil? || kid.is_a?(String)
21
32
 
22
- jwk = resolve_key(kid)
33
+ jwk = resolve_key(kid, key_field)
23
34
 
24
35
  raise ::JWT::DecodeError, 'No keys found in jwks' unless @jwks.any?
25
36
  raise ::JWT::DecodeError, "Could not find public key for kid #{kid}" unless jwk
@@ -27,19 +38,34 @@ module JWT
27
38
  jwk.verify_key
28
39
  end
29
40
 
41
+ # Returns the key for the given token
42
+ # @param [JWT::EncodedToken] token the token
43
+ def call(token)
44
+ @key_fields.each do |key_field|
45
+ field_value = token.header[key_field.to_s]
46
+
47
+ return key_for(field_value, key_field) if field_value
48
+ end
49
+
50
+ raise ::JWT::DecodeError, 'No key id (kid) or x5t found from token headers' unless @allow_nil_kid
51
+
52
+ kid = token.header['kid']
53
+ key_for(kid)
54
+ end
55
+
30
56
  private
31
57
 
32
- def resolve_key(kid)
33
- key_matcher = ->(key) { (kid.nil? && @allow_nil_kid) || key[:kid] == kid }
58
+ def resolve_key(kid, key_field)
59
+ key_matcher = ->(key) { (kid.nil? && @allow_nil_kid) || key[key_field] == kid }
34
60
 
35
61
  # First try without invalidation to facilitate application caching
36
- @jwks ||= JWT::JWK::Set.new(@jwks_loader.call(kid: kid))
62
+ @jwks ||= JWT::JWK::Set.new(@jwks_loader.call(key_field => kid))
37
63
  jwk = @jwks.find { |key| key_matcher.call(key) }
38
64
 
39
65
  return jwk if jwk
40
66
 
41
67
  # Second try, invalidate for backwards compatibility
42
- @jwks = JWT::JWK::Set.new(@jwks_loader.call(invalidate: true, kid_not_found: true, kid: kid))
68
+ @jwks = JWT::JWK::Set.new(@jwks_loader.call(invalidate: true, kid_not_found: true, key_field => kid))
43
69
  @jwks.find { |key| key_matcher.call(key) }
44
70
  end
45
71
  end
data/lib/jwt/jwk/rsa.rb CHANGED
@@ -51,6 +51,7 @@ module JWT
51
51
  def export(options = {})
52
52
  exported = parameters.clone
53
53
  exported.reject! { |k, _| RSA_PRIVATE_KEY_ELEMENTS.include? k } unless private? && options[:include_private] == true
54
+
54
55
  exported
55
56
  end
56
57
 
@@ -67,7 +68,7 @@ module JWT
67
68
  def []=(key, value)
68
69
  raise ArgumentError, 'cannot overwrite cryptographic key attributes' if RSA_KEY_ELEMENTS.include?(key.to_sym)
69
70
 
70
- super(key, value)
71
+ super
71
72
  end
72
73
 
73
74
  private
@@ -165,6 +166,8 @@ module JWT
165
166
  end
166
167
  end
167
168
 
169
+ # :nocov:
170
+ # Before openssl 2.0, we need to use the accessors to set the key
168
171
  def create_rsa_key_using_accessors(rsa_parameters) # rubocop:disable Metrics/AbcSize
169
172
  validate_rsa_parameters!(rsa_parameters)
170
173
 
@@ -179,6 +182,7 @@ module JWT
179
182
  rsa_key.iqmp = rsa_parameters[:qi] if rsa_parameters[:qi]
180
183
  end
181
184
  end
185
+ # :nocov:
182
186
 
183
187
  def validate_rsa_parameters!(rsa_parameters)
184
188
  return unless rsa_parameters.key?(:d)
@@ -191,7 +195,7 @@ module JWT
191
195
 
192
196
  if ::JWT.openssl_3?
193
197
  alias create_rsa_key create_rsa_key_using_der
194
- elsif OpenSSL::PKey::RSA.new.respond_to?(:set_key)
198
+ elsif OpenSSL::PKey::RSA.method_defined?(:set_key)
195
199
  alias create_rsa_key create_rsa_key_using_sets
196
200
  else
197
201
  alias create_rsa_key create_rsa_key_using_accessors
data/lib/jwt/jwk.rb CHANGED
@@ -53,4 +53,3 @@ require_relative 'jwk/key_base'
53
53
  require_relative 'jwk/ec'
54
54
  require_relative 'jwk/rsa'
55
55
  require_relative 'jwk/hmac'
56
- require_relative 'jwk/okp_rbnacl' if JWT.rbnacl?
data/lib/jwt/token.rb CHANGED
@@ -15,8 +15,6 @@ module JWT
15
15
  # token.header # => {"custom"=>"value", "alg"=>"HS256"}
16
16
  #
17
17
  class Token
18
- include Claims::VerificationMethods
19
-
20
18
  # Initializes a new Token instance.
21
19
  #
22
20
  # @param header [Hash] the header of the JWT token.
@@ -89,21 +87,42 @@ module JWT
89
87
 
90
88
  # Signs the JWT token.
91
89
  #
90
+ # @param key [String, JWT::JWK::KeyBase] the key to use for signing.
92
91
  # @param algorithm [String, Object] the algorithm to use for signing.
93
- # @param key [String] the key to use for signing.
94
92
  # @return [void]
95
93
  # @raise [JWT::EncodeError] if the token is already signed or other problems when signing
96
- def sign!(algorithm:, key:)
94
+ def sign!(key:, algorithm:)
97
95
  raise ::JWT::EncodeError, 'Token already signed' if @signature
98
96
 
99
- JWA.resolve(algorithm).tap do |algo|
100
- header.merge!(algo.header)
101
- @signature = algo.sign(data: signing_input, signing_key: key)
97
+ JWA.create_signer(algorithm: algorithm, key: key).tap do |signer|
98
+ header.merge!(signer.jwa.header) { |_key, old, _new| old }
99
+ @signature = signer.sign(data: signing_input)
102
100
  end
103
101
 
104
102
  nil
105
103
  end
106
104
 
105
+ # Verifies the claims of the token.
106
+ # @param options [Array<Symbol>, Hash] the claims to verify.
107
+ # @raise [JWT::DecodeError] if the claims are invalid.
108
+ def verify_claims!(*options)
109
+ Claims::Verifier.verify!(self, *options)
110
+ end
111
+
112
+ # Returns the errors of the claims of the token.
113
+ # @param options [Array<Symbol>, Hash] the claims to verify.
114
+ # @return [Array<Symbol>] the errors of the claims.
115
+ def claim_errors(*options)
116
+ Claims::Verifier.errors(self, *options)
117
+ end
118
+
119
+ # Returns whether the claims of the token are valid.
120
+ # @param options [Array<Symbol>, Hash] the claims to verify.
121
+ # @return [Boolean] whether the claims are valid.
122
+ def valid_claims?(*options)
123
+ claim_errors(*options).empty?
124
+ end
125
+
107
126
  # Returns the JWT token as a string.
108
127
  #
109
128
  # @return [String] the JWT token as a string.
data/lib/jwt/version.rb CHANGED
@@ -12,11 +12,11 @@ module JWT
12
12
  Gem::Version.new(VERSION::STRING)
13
13
  end
14
14
 
15
- # @api private
15
+ # Version constants
16
16
  module VERSION
17
- MAJOR = 2
18
- MINOR = 10
19
- TINY = 2
17
+ MAJOR = 3
18
+ MINOR = 2
19
+ TINY = 0
20
20
  PRE = nil
21
21
 
22
22
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
@@ -32,30 +32,6 @@ module JWT
32
32
  true if 3 * 0x10000000 <= OpenSSL::OPENSSL_VERSION_NUMBER
33
33
  end
34
34
 
35
- # Checks if the RbNaCl library is defined.
36
- #
37
- # @return [Boolean] true if RbNaCl is defined, false otherwise.
38
- # @api private
39
- def self.rbnacl?
40
- defined?(::RbNaCl)
41
- end
42
-
43
- # Checks if the RbNaCl library version is 6.0.0 or greater.
44
- #
45
- # @return [Boolean] true if RbNaCl version is 6.0.0 or greater, false otherwise.
46
- # @api private
47
- def self.rbnacl_6_or_greater?
48
- rbnacl? && ::Gem::Version.new(::RbNaCl::VERSION) >= ::Gem::Version.new('6.0.0')
49
- end
50
-
51
- # Checks if there is an OpenSSL 3 HMAC empty key regression.
52
- #
53
- # @return [Boolean] true if there is an OpenSSL 3 HMAC empty key regression, false otherwise.
54
- # @api private
55
- def self.openssl_3_hmac_empty_key_regression?
56
- openssl_3? && openssl_version <= ::Gem::Version.new('3.0.0')
57
- end
58
-
59
35
  # Returns the OpenSSL version.
60
36
  #
61
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|
data/lib/jwt.rb CHANGED
@@ -5,7 +5,6 @@ require 'jwt/base64'
5
5
  require 'jwt/json'
6
6
  require 'jwt/decode'
7
7
  require 'jwt/configuration'
8
- require 'jwt/deprecations'
9
8
  require 'jwt/encode'
10
9
  require 'jwt/error'
11
10
  require 'jwt/jwk'
@@ -13,9 +12,6 @@ require 'jwt/claims'
13
12
  require 'jwt/encoded_token'
14
13
  require 'jwt/token'
15
14
 
16
- require 'jwt/claims_validator'
17
- require 'jwt/verify'
18
-
19
15
  # JSON Web Token implementation
20
16
  #
21
17
  # Should be up to date with the latest spec:
@@ -47,8 +43,6 @@ module JWT
47
43
  # @param options [Hash] additional options for decoding.
48
44
  # @return [Array<Hash>] the decoded payload and headers.
49
45
  def decode(jwt, key = nil, verify = true, options = {}, &keyfinder) # rubocop:disable Style/OptionalBooleanParameter
50
- Deprecations.context do
51
- Decode.new(jwt, key, verify, configuration.decode.to_h.merge(options), &keyfinder).decode_segments
52
- end
46
+ Decode.new(jwt, key, verify, configuration.decode.to_h.merge(options), &keyfinder).decode_segments
53
47
  end
54
48
  end
data/ruby-jwt.gemspec CHANGED
@@ -35,6 +35,7 @@ Gem::Specification.new do |spec|
35
35
 
36
36
  spec.add_development_dependency 'appraisal'
37
37
  spec.add_development_dependency 'bundler'
38
+ spec.add_development_dependency 'irb'
38
39
  spec.add_development_dependency 'logger'
39
40
  spec.add_development_dependency 'rake'
40
41
  spec.add_development_dependency 'rspec'