jwt 2.10.1 → 3.1.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.
data/UPGRADING.md ADDED
@@ -0,0 +1,47 @@
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
+
42
+ - The `sign` and `verify` static methods on all the algorithms (`::JWT::JWA`) will be removed.
43
+ - Custom algorithms are expected to include the `JWT::JWA::SigningAlgorithm` module.
44
+
45
+ ## Base64 the `k´ value for HMAC JWKs
46
+
47
+ 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
@@ -6,6 +6,11 @@ require 'jwt/x5c_key_finder'
6
6
  module JWT
7
7
  # The Decode class is responsible for decoding and verifying JWT tokens.
8
8
  class Decode
9
+ # Order is very important - first check for string keys, next for symbols
10
+ ALGORITHM_KEYS = ['algorithm',
11
+ :algorithm,
12
+ 'algorithms',
13
+ :algorithms].freeze
9
14
  # Initializes a new Decode instance.
10
15
  #
11
16
  # @param jwt [String] the JWT to decode.
@@ -33,10 +38,10 @@ module JWT
33
38
  verify_algo
34
39
  set_key
35
40
  verify_signature
36
- Claims::DecodeVerifier.verify!(token.payload, @options)
41
+ Claims::DecodeVerifier.verify!(token.unverified_payload, @options)
37
42
  end
38
43
 
39
- [token.payload, token.header]
44
+ [token.unverified_payload, token.header]
40
45
  end
41
46
 
42
47
  private
@@ -60,7 +65,14 @@ module JWT
60
65
 
61
66
  def set_key
62
67
  @key = find_key(&@keyfinder) if @keyfinder
63
- @key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks], allow_nil_kid: @options[:allow_nil_kid]).key_for(token.header['kid']) if @options[:jwks]
68
+ if @options[:jwks]
69
+ @key = ::JWT::JWK::KeyFinder.new(
70
+ jwks: @options[:jwks],
71
+ allow_nil_kid: @options[:allow_nil_kid],
72
+ key_fields: @options[:key_fields]
73
+ ).call(token)
74
+ end
75
+
64
76
  return unless (x5c_options = @options[:x5c])
65
77
 
66
78
  @key = X5cKeyFinder.new(x5c_options[:root_certificates], x5c_options[:crls]).from(token.header['x5c'])
@@ -70,18 +82,9 @@ module JWT
70
82
  @allowed_and_valid_algorithms ||= allowed_algorithms.select { |alg| alg.valid_alg?(alg_in_header) }
71
83
  end
72
84
 
73
- # Order is very important - first check for string keys, next for symbols
74
- ALGORITHM_KEYS = ['algorithm',
75
- :algorithm,
76
- 'algorithms',
77
- :algorithms].freeze
78
-
79
85
  def given_algorithms
80
- ALGORITHM_KEYS.each do |alg_key|
81
- alg = @options[alg_key]
82
- return Array(alg) if alg
83
- end
84
- []
86
+ alg_key = ALGORITHM_KEYS.find { |key| @options[key] }
87
+ Array(@options[alg_key])
85
88
  end
86
89
 
87
90
  def allowed_algorithms
@@ -93,7 +96,7 @@ module JWT
93
96
  end
94
97
 
95
98
  def find_key(&keyfinder)
96
- key = (keyfinder.arity == 2 ? yield(token.header, token.payload) : yield(token.header))
99
+ key = (keyfinder.arity == 2 ? yield(token.header, token.unverified_payload) : yield(token.header))
97
100
  # key can be of type [string, nil, OpenSSL::PKey, Array]
98
101
  return key if key && !Array(key).empty?
99
102
 
@@ -12,7 +12,25 @@ 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
30
+
31
+ DEFAULT_CLAIMS = [:exp].freeze
32
+
33
+ private_constant(:DEFAULT_CLAIMS)
16
34
 
17
35
  # Returns the original token provided to the class.
18
36
  # @return [String] The JWT token.
@@ -26,6 +44,9 @@ module JWT
26
44
  raise ArgumentError, 'Provided JWT must be a String' unless jwt.is_a?(String)
27
45
 
28
46
  @jwt = jwt
47
+ @signature_verified = false
48
+ @claims_verified = false
49
+
29
50
  @encoded_header, @encoded_payload, @encoded_signature = jwt.split('.')
30
51
  end
31
52
 
@@ -53,11 +74,21 @@ module JWT
53
74
  # @return [String] the encoded header.
54
75
  attr_reader :encoded_header
55
76
 
56
- # Returns the payload of the JWT token.
77
+ # Returns the payload of the JWT token. Access requires the signature and claims to have been verified.
57
78
  #
58
79
  # @return [Hash] the payload.
80
+ # @raise [JWT::DecodeError] if the signature has not been verified.
59
81
  def payload
60
- @payload ||= decode_payload
82
+ raise JWT::DecodeError, 'Verify the token signature before accessing the payload' unless @signature_verified
83
+ raise JWT::DecodeError, 'Verify the token claims before accessing the payload' unless @claims_verified
84
+
85
+ decoded_payload
86
+ end
87
+
88
+ # Returns the payload of the JWT token without requiring the signature to have been verified.
89
+ # @return [Hash] the payload.
90
+ def unverified_payload
91
+ decoded_payload
61
92
  end
62
93
 
63
94
  # Sets or returns the encoded payload of the JWT token.
@@ -72,6 +103,33 @@ module JWT
72
103
  [encoded_header, encoded_payload].join('.')
73
104
  end
74
105
 
106
+ # Verifies the token signature and claims.
107
+ # By default it verifies the 'exp' claim.
108
+ #
109
+ # @example
110
+ # encoded_token.verify!(signature: { algorithm: 'HS256', key: 'secret' }, claims: [:exp])
111
+ #
112
+ # @param signature [Hash] the parameters for signature verification (see {#verify_signature!}).
113
+ # @param claims [Array<Symbol>, Hash] the claims to verify (see {#verify_claims!}).
114
+ # @return [nil]
115
+ # @raise [JWT::DecodeError] if the signature or claim verification fails.
116
+ def verify!(signature:, claims: nil)
117
+ verify_signature!(**signature)
118
+ claims.is_a?(Array) ? verify_claims!(*claims) : verify_claims!(claims)
119
+ nil
120
+ end
121
+
122
+ # Verifies the token signature and claims.
123
+ # By default it verifies the 'exp' claim.
124
+
125
+ # @param signature [Hash] the parameters for signature verification (see {#verify_signature!}).
126
+ # @param claims [Array<Symbol>, Hash] the claims to verify (see {#verify_claims!}).
127
+ # @return [Boolean] true if the signature and claims are valid, false otherwise.
128
+ def valid?(signature:, claims: nil)
129
+ valid_signature?(**signature) &&
130
+ (claims.is_a?(Array) ? valid_claims?(*claims) : valid_claims?(claims))
131
+ end
132
+
75
133
  # Verifies the signature of the JWT token.
76
134
  #
77
135
  # @param algorithm [String, Array<String>, Object, Array<Object>] the algorithm(s) to use for verification.
@@ -80,12 +138,8 @@ module JWT
80
138
  # @return [nil]
81
139
  # @raise [JWT::VerificationError] if the signature verification fails.
82
140
  # @raise [ArgumentError] if neither key nor key_finder is provided, or if both are provided.
83
- def verify_signature!(algorithm:, key: nil, key_finder: nil)
84
- raise ArgumentError, 'Provide either key or key_finder, not both or neither' if key.nil? == key_finder.nil?
85
-
86
- key ||= key_finder.call(self)
87
-
88
- return if valid_signature?(algorithm: algorithm, key: key)
141
+ def verify_signature!(algorithm: nil, key: nil, key_finder: nil)
142
+ return if valid_signature?(algorithm: algorithm, key: key, key_finder: key_finder)
89
143
 
90
144
  raise JWT::VerificationError, 'Signature verification failed'
91
145
  end
@@ -93,20 +147,59 @@ module JWT
93
147
  # Checks if the signature of the JWT token is valid.
94
148
  #
95
149
  # @param algorithm [String, Array<String>, Object, Array<Object>] the algorithm(s) to use for verification.
96
- # @param key [String, Array<String>] the key(s) to use for verification.
150
+ # @param key [String, Array<String>, JWT::JWK::KeyBase, Array<JWT::JWK::KeyBase>] the key(s) to use for verification.
151
+ # @param key_finder [#call] an object responding to `call` to find the key for verification.
97
152
  # @return [Boolean] true if the signature is valid, false otherwise.
98
- def valid_signature?(algorithm:, key:)
99
- Array(JWA.resolve_and_sort(algorithms: algorithm, preferred_algorithm: header['alg'])).any? do |algo|
100
- Array(key).any? do |one_key|
101
- algo.verify(data: signing_input, signature: signature, verification_key: one_key)
102
- end
153
+ def valid_signature?(algorithm: nil, key: nil, key_finder: nil)
154
+ raise ArgumentError, 'Provide either key or key_finder, not both or neither' if key.nil? == key_finder.nil?
155
+
156
+ keys = Array(key || key_finder.call(self))
157
+ verifiers = JWA.create_verifiers(algorithms: algorithm, keys: keys, preferred_algorithm: header['alg'])
158
+
159
+ raise JWT::VerificationError, 'No algorithm provided' if verifiers.empty?
160
+
161
+ valid = verifiers.any? do |jwa|
162
+ jwa.verify(data: signing_input, signature: signature)
103
163
  end
164
+ valid.tap { |verified| @signature_verified = verified }
165
+ end
166
+
167
+ # Verifies the claims of the token.
168
+ # @param options [Array<Symbol>, Hash] the claims to verify. By default, it checks the 'exp' claim.
169
+ # @raise [JWT::DecodeError] if the claims are invalid.
170
+ def verify_claims!(*options)
171
+ Claims::Verifier.verify!(ClaimsContext.new(self), *claims_options(options)).tap do
172
+ @claims_verified = true
173
+ end
174
+ rescue StandardError
175
+ @claims_verified = false
176
+ raise
177
+ end
178
+
179
+ # Returns the errors of the claims of the token.
180
+ # @param options [Array<Symbol>, Hash] the claims to verify. By default, it checks the 'exp' claim.
181
+ # @return [Array<Symbol>] the errors of the claims.
182
+ def claim_errors(*options)
183
+ Claims::Verifier.errors(ClaimsContext.new(self), *claims_options(options))
184
+ end
185
+
186
+ # Returns whether the claims of the token are valid.
187
+ # @param options [Array<Symbol>, Hash] the claims to verify. By default, it checks the 'exp' claim.
188
+ # @return [Boolean] whether the claims are valid.
189
+ def valid_claims?(*options)
190
+ claim_errors(*claims_options(options)).empty?.tap { |verified| @claims_verified = verified }
104
191
  end
105
192
 
106
193
  alias to_s jwt
107
194
 
108
195
  private
109
196
 
197
+ def claims_options(options)
198
+ return DEFAULT_CLAIMS if options.first.nil?
199
+
200
+ options
201
+ end
202
+
110
203
  def decode_payload
111
204
  raise JWT::DecodeError, 'Encoded payload is empty' if encoded_payload == ''
112
205
 
@@ -135,5 +228,9 @@ module JWT
135
228
  rescue ::JSON::ParserError
136
229
  raise JWT::DecodeError, 'Invalid segment encoding'
137
230
  end
231
+
232
+ def decoded_payload
233
+ @decoded_payload ||= decode_payload
234
+ end
138
235
  end
139
236
  end
data/lib/jwt/error.rb CHANGED
@@ -7,9 +7,6 @@ module JWT
7
7
  # The DecodeError class is raised when there is an error decoding a JWT.
8
8
  class DecodeError < StandardError; end
9
9
 
10
- # The RequiredDependencyError class is raised when a required dependency is missing.
11
- class RequiredDependencyError < StandardError; end
12
-
13
10
  # The VerificationError class is raised when there is an error verifying a JWT.
14
11
  class VerificationError < DecodeError; end
15
12
 
data/lib/jwt/jwa/ecdsa.rb CHANGED
@@ -12,14 +12,22 @@ module JWT
12
12
  end
13
13
 
14
14
  def sign(data:, signing_key:)
15
+ raise_sign_error!("The given key is a #{signing_key.class}. It has to be an OpenSSL::PKey::EC instance") unless signing_key.is_a?(::OpenSSL::PKey::EC)
16
+ raise_sign_error!('The given key is not a private key') unless signing_key.private?
17
+
15
18
  curve_definition = curve_by_name(signing_key.group.curve_name)
16
19
  key_algorithm = curve_definition[:algorithm]
20
+
17
21
  raise IncorrectAlgorithm, "payload algorithm is #{alg} but #{key_algorithm} signing key was provided" if alg != key_algorithm
18
22
 
19
23
  asn1_to_raw(signing_key.dsa_sign_asn1(digest.digest(data)), signing_key)
20
24
  end
21
25
 
22
26
  def verify(data:, signature:, verification_key:)
27
+ verification_key = self.class.create_public_key_from_point(verification_key) if verification_key.is_a?(::OpenSSL::PKey::EC::Point)
28
+
29
+ raise_verify_error!("The given key is a #{verification_key.class}. It has to be an OpenSSL::PKey::EC instance") unless verification_key.is_a?(::OpenSSL::PKey::EC)
30
+
23
31
  curve_definition = curve_by_name(verification_key.group.curve_name)
24
32
  key_algorithm = curve_definition[:algorithm]
25
33
  raise IncorrectAlgorithm, "payload algorithm is #{alg} but #{key_algorithm} verification key was provided" if alg != key_algorithm
@@ -56,16 +64,29 @@ module JWT
56
64
  register_algorithm(new(v[:algorithm], v[:digest]))
57
65
  end
58
66
 
59
- def self.from_algorithm(algorithm)
60
- new(algorithm, algorithm.downcase.gsub('es', 'sha'))
61
- end
62
-
67
+ # @api private
63
68
  def self.curve_by_name(name)
64
69
  NAMED_CURVES.fetch(name) do
65
70
  raise UnsupportedEcdsaCurve, "The ECDSA curve '#{name}' is not supported"
66
71
  end
67
72
  end
68
73
 
74
+ if ::JWT.openssl_3?
75
+ def self.create_public_key_from_point(point)
76
+ sequence = OpenSSL::ASN1::Sequence([
77
+ OpenSSL::ASN1::Sequence([OpenSSL::ASN1::ObjectId('id-ecPublicKey'), OpenSSL::ASN1::ObjectId(point.group.curve_name)]),
78
+ OpenSSL::ASN1::BitString(point.to_octet_string(:uncompressed))
79
+ ])
80
+ OpenSSL::PKey::EC.new(sequence.to_der)
81
+ end
82
+ else
83
+ def self.create_public_key_from_point(point)
84
+ OpenSSL::PKey::EC.new(point.group.curve_name).tap do |key|
85
+ key.public_key = point
86
+ end
87
+ end
88
+ end
89
+
69
90
  private
70
91
 
71
92
  attr_reader :digest
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,42 +9,73 @@ 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.
31
15
  module JWA
16
+ # @api private
17
+ class VerifierContext
18
+ def initialize(jwa:, keys:)
19
+ @jwa = jwa
20
+ @keys = Array(keys)
21
+ end
22
+
23
+ def verify(*args, **kwargs)
24
+ @keys.any? do |key|
25
+ @jwa.verify(*args, **kwargs, verification_key: key)
26
+ end
27
+ end
28
+ end
29
+
30
+ # @api private
31
+ class SignerContext
32
+ def initialize(jwa:, key:)
33
+ @jwa = jwa
34
+ @key = key
35
+ end
36
+
37
+ def sign(*args, **kwargs)
38
+ @jwa.sign(*args, **kwargs, signing_key: @key)
39
+ end
40
+
41
+ def jwa_header
42
+ @jwa.header
43
+ end
44
+ end
45
+
32
46
  class << self
33
47
  # @api private
34
48
  def resolve(algorithm)
35
49
  return find(algorithm) if algorithm.is_a?(String) || algorithm.is_a?(Symbol)
36
50
 
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
51
+ raise ArgumentError, 'Algorithm must be provided' if algorithm.nil?
52
+
53
+ raise ArgumentError, 'Custom algorithms are required to include JWT::JWA::SigningAlgorithm' unless algorithm.is_a?(SigningAlgorithm)
41
54
 
42
55
  algorithm
43
56
  end
44
57
 
45
58
  # @api private
46
59
  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
60
+ Array(algorithms).map { |alg| JWA.resolve(alg) }
61
+ .partition { |alg| alg.valid_alg?(preferred_algorithm) }
62
+ .flatten
49
63
  end
50
64
 
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)
65
+ # @api private
66
+ def create_signer(algorithm:, key:)
67
+ return key if key.is_a?(JWK::KeyBase)
68
+
69
+ SignerContext.new(jwa: resolve(algorithm), key: key)
70
+ end
71
+
72
+ # @api private
73
+ def create_verifiers(algorithms:, keys:, preferred_algorithm:)
74
+ jwks, other_keys = keys.partition { |key| key.is_a?(JWK::KeyBase) }
75
+
76
+ jwks + resolve_and_sort(algorithms: algorithms,
77
+ preferred_algorithm: preferred_algorithm)
78
+ .map { |jwa| VerifierContext.new(jwa: jwa, keys: other_keys) }
55
79
  end
56
80
  end
57
81
  end