jwt 2.8.1 → 2.10.3

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +96 -0
  3. data/README.md +189 -93
  4. data/lib/jwt/base64.rb +4 -1
  5. data/lib/jwt/claims/audience.rb +30 -0
  6. data/lib/jwt/claims/crit.rb +35 -0
  7. data/lib/jwt/claims/decode_verifier.rb +40 -0
  8. data/lib/jwt/claims/expiration.rb +32 -0
  9. data/lib/jwt/claims/issued_at.rb +22 -0
  10. data/lib/jwt/claims/issuer.rb +34 -0
  11. data/lib/jwt/claims/jwt_id.rb +35 -0
  12. data/lib/jwt/claims/not_before.rb +32 -0
  13. data/lib/jwt/claims/numeric.rb +77 -0
  14. data/lib/jwt/claims/required.rb +33 -0
  15. data/lib/jwt/claims/subject.rb +30 -0
  16. data/lib/jwt/claims/verification_methods.rb +20 -0
  17. data/lib/jwt/claims/verifier.rb +61 -0
  18. data/lib/jwt/claims.rb +74 -0
  19. data/lib/jwt/claims_validator.rb +6 -25
  20. data/lib/jwt/configuration/container.rb +20 -0
  21. data/lib/jwt/configuration/decode_configuration.rb +24 -0
  22. data/lib/jwt/configuration/jwk_configuration.rb +2 -1
  23. data/lib/jwt/configuration.rb +8 -0
  24. data/lib/jwt/decode.rb +34 -76
  25. data/lib/jwt/deprecations.rb +25 -5
  26. data/lib/jwt/encode.rb +17 -60
  27. data/lib/jwt/encoded_token.rb +139 -0
  28. data/lib/jwt/error.rb +34 -0
  29. data/lib/jwt/json.rb +1 -1
  30. data/lib/jwt/jwa/compat.rb +32 -0
  31. data/lib/jwt/jwa/ecdsa.rb +39 -25
  32. data/lib/jwt/jwa/eddsa.rb +20 -27
  33. data/lib/jwt/jwa/hmac.rb +28 -19
  34. data/lib/jwt/jwa/hmac_rbnacl.rb +43 -43
  35. data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +40 -39
  36. data/lib/jwt/jwa/none.rb +8 -3
  37. data/lib/jwt/jwa/ps.rb +20 -15
  38. data/lib/jwt/jwa/rsa.rb +20 -10
  39. data/lib/jwt/jwa/signing_algorithm.rb +63 -0
  40. data/lib/jwt/jwa/unsupported.rb +9 -8
  41. data/lib/jwt/jwa/wrapper.rb +27 -9
  42. data/lib/jwt/jwa.rb +30 -34
  43. data/lib/jwt/jwk/ec.rb +22 -23
  44. data/lib/jwt/jwk/hmac.rb +2 -3
  45. data/lib/jwt/jwk/key_base.rb +1 -0
  46. data/lib/jwt/jwk/key_finder.rb +5 -4
  47. data/lib/jwt/jwk/kid_as_key_digest.rb +1 -0
  48. data/lib/jwt/jwk/okp_rbnacl.rb +3 -4
  49. data/lib/jwt/jwk/rsa.rb +2 -3
  50. data/lib/jwt/jwk/set.rb +3 -1
  51. data/lib/jwt/jwk.rb +1 -0
  52. data/lib/jwt/token.rb +112 -0
  53. data/lib/jwt/verify.rb +16 -93
  54. data/lib/jwt/version.rb +31 -10
  55. data/lib/jwt/x5c_key_finder.rb +2 -2
  56. data/lib/jwt.rb +23 -1
  57. data/ruby-jwt.gemspec +1 -0
  58. metadata +36 -7
data/lib/jwt/jwa/eddsa.rb CHANGED
@@ -2,41 +2,34 @@
2
2
 
3
3
  module JWT
4
4
  module JWA
5
- module Eddsa
6
- SUPPORTED = %w[ED25519 EdDSA].freeze
7
- SUPPORTED_DOWNCASED = SUPPORTED.map(&:downcase).freeze
5
+ # Implementation of the EdDSA family of algorithms
6
+ class Eddsa
7
+ include JWT::JWA::SigningAlgorithm
8
8
 
9
- class << self
10
- def sign(algorithm, msg, key)
11
- unless key.is_a?(RbNaCl::Signatures::Ed25519::SigningKey)
12
- raise EncodeError, "Key given is a #{key.class} but has to be an RbNaCl::Signatures::Ed25519::SigningKey"
13
- end
14
-
15
- validate_algorithm!(algorithm)
16
-
17
- key.sign(msg)
18
- end
9
+ def initialize(alg)
10
+ @alg = alg
11
+ end
19
12
 
20
- def verify(algorithm, public_key, signing_input, signature)
21
- unless public_key.is_a?(RbNaCl::Signatures::Ed25519::VerifyKey)
22
- raise DecodeError, "key given is a #{public_key.class} but has to be a RbNaCl::Signatures::Ed25519::VerifyKey"
23
- end
13
+ def sign(data:, signing_key:)
14
+ raise_sign_error!("Key given is a #{signing_key.class} but has to be an RbNaCl::Signatures::Ed25519::SigningKey") unless signing_key.is_a?(RbNaCl::Signatures::Ed25519::SigningKey)
24
15
 
25
- validate_algorithm!(algorithm)
16
+ Deprecations.warning('Using the EdDSA algorithm is deprecated and will be removed in a future version of ruby-jwt. In the future the algorithm will be provided by the jwt-eddsa gem.')
26
17
 
27
- public_key.verify(signature, signing_input)
28
- rescue RbNaCl::CryptoError
29
- false
30
- end
18
+ signing_key.sign(data)
19
+ end
31
20
 
32
- private
21
+ def verify(data:, signature:, verification_key:)
22
+ raise_verify_error!("key given is a #{verification_key.class} but has to be a RbNaCl::Signatures::Ed25519::VerifyKey") unless verification_key.is_a?(RbNaCl::Signatures::Ed25519::VerifyKey)
33
23
 
34
- def validate_algorithm!(algorithm)
35
- return if SUPPORTED_DOWNCASED.include?(algorithm.downcase)
24
+ Deprecations.warning('Using the EdDSA algorithm is deprecated and will be removed in a future version of ruby-jwt. In the future the algorithm will be provided by the jwt-eddsa gem.')
36
25
 
37
- raise IncorrectAlgorithm, "Algorithm #{algorithm} not supported. Supported algoritms are #{SUPPORTED.join(', ')}"
38
- end
26
+ verification_key.verify(signature, data)
27
+ rescue RbNaCl::CryptoError
28
+ false
39
29
  end
30
+
31
+ register_algorithm(new('ED25519'))
32
+ register_algorithm(new('EdDSA'))
40
33
  end
41
34
  end
42
35
  end
data/lib/jwt/jwa/hmac.rb CHANGED
@@ -2,33 +2,42 @@
2
2
 
3
3
  module JWT
4
4
  module JWA
5
- module Hmac
6
- module_function
5
+ # Implementation of the HMAC family of algorithms
6
+ class Hmac
7
+ include JWT::JWA::SigningAlgorithm
7
8
 
8
- MAPPING = {
9
- 'HS256' => OpenSSL::Digest::SHA256,
10
- 'HS384' => OpenSSL::Digest::SHA384,
11
- 'HS512' => OpenSSL::Digest::SHA512
12
- }.freeze
9
+ def self.from_algorithm(algorithm)
10
+ new(algorithm, OpenSSL::Digest.new(algorithm.downcase.gsub('hs', 'sha')))
11
+ end
13
12
 
14
- SUPPORTED = MAPPING.keys
13
+ def initialize(alg, digest)
14
+ @alg = alg
15
+ @digest = digest
16
+ end
15
17
 
16
- def sign(algorithm, msg, key)
17
- key ||= ''
18
+ def sign(data:, signing_key:)
19
+ ensure_valid_key!(signing_key)
18
20
 
19
- raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
21
+ OpenSSL::HMAC.digest(digest.new, signing_key, data)
22
+ end
20
23
 
21
- OpenSSL::HMAC.digest(MAPPING[algorithm].new, key, msg)
22
- rescue OpenSSL::HMACError => e
23
- if key == '' && e.message == 'EVP_PKEY_new_mac_key: malloc failure'
24
- raise JWT::DecodeError, 'OpenSSL 3.0 does not support nil or empty hmac_secret'
25
- end
24
+ def verify(data:, signature:, verification_key:)
25
+ ensure_valid_key!(verification_key)
26
26
 
27
- raise e
27
+ SecurityUtils.secure_compare(signature, OpenSSL::HMAC.digest(digest.new, verification_key, data))
28
28
  end
29
29
 
30
- def verify(algorithm, key, signing_input, signature)
31
- SecurityUtils.secure_compare(signature, sign(algorithm, signing_input, key))
30
+ register_algorithm(new('HS256', OpenSSL::Digest::SHA256))
31
+ register_algorithm(new('HS384', OpenSSL::Digest::SHA384))
32
+ register_algorithm(new('HS512', OpenSSL::Digest::SHA512))
33
+
34
+ private
35
+
36
+ attr_reader :digest
37
+
38
+ def ensure_valid_key!(key)
39
+ raise_verify_error!('HMAC key expected to be a String') unless key.is_a?(String)
40
+ raise_verify_error!('HMAC key cannot be empty') if key.empty?
32
41
  end
33
42
 
34
43
  # Copy of https://github.com/rails/rails/blob/v7.0.3.1/activesupport/lib/active_support/security_utils.rb
@@ -1,49 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWT
4
- module Algos
5
- module HmacRbNaCl
6
- MAPPING = { 'HS512256' => ::RbNaCl::HMAC::SHA512256 }.freeze
7
- SUPPORTED = MAPPING.keys
8
- class << self
9
- def sign(algorithm, msg, key)
10
- Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
11
- if (hmac = resolve_algorithm(algorithm))
12
- hmac.auth(key_for_rbnacl(hmac, key).encode('binary'), msg.encode('binary'))
13
- else
14
- Hmac.sign(algorithm, msg, key)
15
- end
16
- end
17
-
18
- def verify(algorithm, key, signing_input, signature)
19
- Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
20
- if (hmac = resolve_algorithm(algorithm))
21
- hmac.verify(key_for_rbnacl(hmac, key).encode('binary'), signature.encode('binary'), signing_input.encode('binary'))
22
- else
23
- Hmac.verify(algorithm, key, signing_input, signature)
24
- end
25
- rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
26
- false
27
- end
28
-
29
- private
30
-
31
- def key_for_rbnacl(hmac, key)
32
- key ||= ''
33
- raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
34
-
35
- return padded_empty_key(hmac.key_bytes) if key == ''
36
-
37
- key
38
- end
39
-
40
- def resolve_algorithm(algorithm)
41
- MAPPING.fetch(algorithm)
42
- end
43
-
44
- def padded_empty_key(length)
45
- Array.new(length, 0x0).pack('C*').encode('binary')
46
- end
4
+ module JWA
5
+ # Implementation of the HMAC family of algorithms (using RbNaCl)
6
+ class HmacRbNaCl
7
+ include JWT::JWA::SigningAlgorithm
8
+
9
+ def self.from_algorithm(algorithm)
10
+ new(algorithm, ::RbNaCl::HMAC.const_get(algorithm.upcase.gsub('HS', 'SHA')))
11
+ end
12
+
13
+ def initialize(alg, hmac)
14
+ @alg = alg
15
+ @hmac = hmac
16
+ end
17
+
18
+ def sign(data:, signing_key:)
19
+ Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
20
+ hmac.auth(key_for_rbnacl(hmac, signing_key).encode('binary'), data.encode('binary'))
21
+ end
22
+
23
+ def verify(data:, signature:, verification_key:)
24
+ Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
25
+ hmac.verify(key_for_rbnacl(hmac, verification_key).encode('binary'), signature.encode('binary'), data.encode('binary'))
26
+ rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
27
+ false
28
+ end
29
+
30
+ register_algorithm(new('HS512256', ::RbNaCl::HMAC::SHA512256))
31
+
32
+ private
33
+
34
+ attr_reader :hmac
35
+
36
+ def key_for_rbnacl(hmac, key)
37
+ key ||= ''
38
+ raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
39
+
40
+ return padded_empty_key(hmac.key_bytes) if key == ''
41
+
42
+ key
43
+ end
44
+
45
+ def padded_empty_key(length)
46
+ Array.new(length, 0x0).pack('C*').encode('binary')
47
47
  end
48
48
  end
49
49
  end
@@ -1,45 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWT
4
- module Algos
5
- module HmacRbNaClFixed
6
- MAPPING = { 'HS512256' => ::RbNaCl::HMAC::SHA512256 }.freeze
7
- SUPPORTED = MAPPING.keys
8
-
9
- class << self
10
- def sign(algorithm, msg, key)
11
- key ||= ''
12
- Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
13
- raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
14
-
15
- if (hmac = resolve_algorithm(algorithm)) && key.bytesize <= hmac.key_bytes
16
- hmac.auth(padded_key_bytes(key, hmac.key_bytes), msg.encode('binary'))
17
- else
18
- Hmac.sign(algorithm, msg, key)
19
- end
20
- end
21
-
22
- def verify(algorithm, key, signing_input, signature)
23
- key ||= ''
24
- Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
25
- raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
26
-
27
- if (hmac = resolve_algorithm(algorithm)) && key.bytesize <= hmac.key_bytes
28
- hmac.verify(padded_key_bytes(key, hmac.key_bytes), signature.encode('binary'), signing_input.encode('binary'))
29
- else
30
- Hmac.verify(algorithm, key, signing_input, signature)
31
- end
32
- rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
33
- false
34
- end
35
-
36
- def resolve_algorithm(algorithm)
37
- MAPPING.fetch(algorithm)
38
- end
39
-
40
- def padded_key_bytes(key, bytesize)
41
- key.bytes.fill(0, key.bytesize...bytesize).pack('C*')
42
- end
4
+ module JWA
5
+ # Implementation of the HMAC family of algorithms (using RbNaCl prior to a certain version)
6
+ class HmacRbNaClFixed
7
+ include JWT::JWA::SigningAlgorithm
8
+
9
+ def self.from_algorithm(algorithm)
10
+ new(algorithm, ::RbNaCl::HMAC.const_get(algorithm.upcase.gsub('HS', 'SHA')))
11
+ end
12
+
13
+ def initialize(alg, hmac)
14
+ @alg = alg
15
+ @hmac = hmac
16
+ end
17
+
18
+ def sign(data:, signing_key:)
19
+ signing_key ||= ''
20
+ Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
21
+ raise JWT::DecodeError, 'HMAC key expected to be a String' unless signing_key.is_a?(String)
22
+
23
+ hmac.auth(padded_key_bytes(signing_key, hmac.key_bytes), data.encode('binary'))
24
+ end
25
+
26
+ def verify(data:, signature:, verification_key:)
27
+ verification_key ||= ''
28
+ Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
29
+ raise JWT::DecodeError, 'HMAC key expected to be a String' unless verification_key.is_a?(String)
30
+
31
+ hmac.verify(padded_key_bytes(verification_key, hmac.key_bytes), signature.encode('binary'), data.encode('binary'))
32
+ rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
33
+ false
34
+ end
35
+
36
+ register_algorithm(new('HS512256', ::RbNaCl::HMAC::SHA512256))
37
+
38
+ private
39
+
40
+ attr_reader :hmac
41
+
42
+ def padded_key_bytes(key, bytesize)
43
+ key.bytes.fill(0, key.bytesize...bytesize).pack('C*')
43
44
  end
44
45
  end
45
46
  end
data/lib/jwt/jwa/none.rb CHANGED
@@ -2,10 +2,13 @@
2
2
 
3
3
  module JWT
4
4
  module JWA
5
- module None
6
- module_function
5
+ # Implementation of the none algorithm
6
+ class None
7
+ include JWT::JWA::SigningAlgorithm
7
8
 
8
- SUPPORTED = %w[none].freeze
9
+ def initialize
10
+ @alg = 'none'
11
+ end
9
12
 
10
13
  def sign(*)
11
14
  ''
@@ -14,6 +17,8 @@ module JWT
14
17
  def verify(*)
15
18
  true
16
19
  end
20
+
21
+ register_algorithm(new)
17
22
  end
18
23
  end
19
24
  end
data/lib/jwt/jwa/ps.rb CHANGED
@@ -2,29 +2,34 @@
2
2
 
3
3
  module JWT
4
4
  module JWA
5
- module Ps
6
- # RSASSA-PSS signing algorithms
5
+ # Implementation of the RSASSA-PSS family of algorithms
6
+ class Ps
7
+ include JWT::JWA::SigningAlgorithm
7
8
 
8
- module_function
9
-
10
- SUPPORTED = %w[PS256 PS384 PS512].freeze
11
-
12
- def sign(algorithm, msg, key)
13
- unless key.is_a?(::OpenSSL::PKey::RSA)
14
- raise EncodeError, "The given key is a #{key_class}. It has to be an OpenSSL::PKey::RSA instance."
15
- end
9
+ def initialize(alg)
10
+ @alg = alg
11
+ @digest_algorithm = alg.sub('PS', 'sha')
12
+ end
16
13
 
17
- translated_algorithm = algorithm.sub('PS', 'sha')
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::RSA instance.") unless signing_key.is_a?(::OpenSSL::PKey::RSA)
18
16
 
19
- key.sign_pss(translated_algorithm, msg, salt_length: :digest, mgf1_hash: translated_algorithm)
17
+ signing_key.sign_pss(digest_algorithm, data, salt_length: :digest, mgf1_hash: digest_algorithm)
20
18
  end
21
19
 
22
- def verify(algorithm, public_key, signing_input, signature)
23
- translated_algorithm = algorithm.sub('PS', 'sha')
24
- public_key.verify_pss(translated_algorithm, signature, signing_input, salt_length: :auto, mgf1_hash: translated_algorithm)
20
+ def verify(data:, signature:, verification_key:)
21
+ verification_key.verify_pss(digest_algorithm, signature, data, salt_length: :auto, mgf1_hash: digest_algorithm)
25
22
  rescue OpenSSL::PKey::PKeyError
26
23
  raise JWT::VerificationError, 'Signature verification raised'
27
24
  end
25
+
26
+ register_algorithm(new('PS256'))
27
+ register_algorithm(new('PS384'))
28
+ register_algorithm(new('PS512'))
29
+
30
+ private
31
+
32
+ attr_reader :digest_algorithm
28
33
  end
29
34
  end
30
35
  end
data/lib/jwt/jwa/rsa.rb CHANGED
@@ -2,24 +2,34 @@
2
2
 
3
3
  module JWT
4
4
  module JWA
5
- module Rsa
6
- module_function
5
+ # Implementation of the RSA family of algorithms
6
+ class Rsa
7
+ include JWT::JWA::SigningAlgorithm
7
8
 
8
- SUPPORTED = %w[RS256 RS384 RS512].freeze
9
+ def initialize(alg)
10
+ @alg = alg
11
+ @digest = alg.sub('RS', 'SHA')
12
+ end
9
13
 
10
- def sign(algorithm, msg, key)
11
- unless key.is_a?(OpenSSL::PKey::RSA)
12
- raise EncodeError, "The given key is a #{key.class}. It has to be an OpenSSL::PKey::RSA instance"
13
- end
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::RSA instance") unless signing_key.is_a?(OpenSSL::PKey::RSA)
14
16
 
15
- key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
17
+ signing_key.sign(OpenSSL::Digest.new(digest), data)
16
18
  end
17
19
 
18
- def verify(algorithm, public_key, signing_input, signature)
19
- public_key.verify(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
20
+ def verify(data:, signature:, verification_key:)
21
+ verification_key.verify(OpenSSL::Digest.new(digest), signature, data)
20
22
  rescue OpenSSL::PKey::PKeyError
21
23
  raise JWT::VerificationError, 'Signature verification raised'
22
24
  end
25
+
26
+ register_algorithm(new('RS256'))
27
+ register_algorithm(new('RS384'))
28
+ register_algorithm(new('RS512'))
29
+
30
+ private
31
+
32
+ attr_reader :digest
23
33
  end
24
34
  end
25
35
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ # JSON Web Algorithms
5
+ module JWA
6
+ # Base functionality for signing algorithms
7
+ module SigningAlgorithm
8
+ # Class methods for the SigningAlgorithm module
9
+ module ClassMethods
10
+ def register_algorithm(algo)
11
+ ::JWT::JWA.register_algorithm(algo)
12
+ end
13
+ end
14
+
15
+ def self.included(klass)
16
+ klass.extend(ClassMethods)
17
+ klass.include(JWT::JWA::Compat)
18
+ end
19
+
20
+ attr_reader :alg
21
+
22
+ def valid_alg?(alg_to_check)
23
+ alg&.casecmp(alg_to_check)&.zero? == true
24
+ end
25
+
26
+ def header(*)
27
+ { 'alg' => alg }
28
+ end
29
+
30
+ def sign(*)
31
+ raise_sign_error!('Algorithm implementation is missing the sign method')
32
+ end
33
+
34
+ def verify(*)
35
+ raise_verify_error!('Algorithm implementation is missing the verify method')
36
+ end
37
+
38
+ def raise_verify_error!(message)
39
+ raise(DecodeError.new(message).tap { |e| e.set_backtrace(caller(1)) })
40
+ end
41
+
42
+ def raise_sign_error!(message)
43
+ raise(EncodeError.new(message).tap { |e| e.set_backtrace(caller(1)) })
44
+ end
45
+ end
46
+
47
+ class << self
48
+ def register_algorithm(algo)
49
+ algorithms[algo.alg.to_s.downcase] = algo
50
+ end
51
+
52
+ def find(algo)
53
+ algorithms.fetch(algo.to_s.downcase, Unsupported)
54
+ end
55
+
56
+ private
57
+
58
+ def algorithms
59
+ @algorithms ||= {}
60
+ end
61
+ end
62
+ end
63
+ end
@@ -2,17 +2,18 @@
2
2
 
3
3
  module JWT
4
4
  module JWA
5
+ # Represents an unsupported algorithm
5
6
  module Unsupported
6
- module_function
7
+ class << self
8
+ include JWT::JWA::SigningAlgorithm
7
9
 
8
- SUPPORTED = [].freeze
10
+ def sign(*)
11
+ raise_sign_error!('Unsupported signing method')
12
+ end
9
13
 
10
- def sign(*)
11
- raise NotImplementedError, 'Unsupported signing method'
12
- end
13
-
14
- def verify(*)
15
- raise JWT::VerificationError, 'Algorithm not supported'
14
+ def verify(*)
15
+ raise JWT::VerificationError, 'Algorithm not supported'
16
+ end
16
17
  end
17
18
  end
18
19
  end
@@ -2,24 +2,42 @@
2
2
 
3
3
  module JWT
4
4
  module JWA
5
+ # @api private
5
6
  class Wrapper
6
- attr_reader :alg, :cls
7
+ include SigningAlgorithm
7
8
 
8
- def initialize(alg, cls)
9
- @alg = alg
10
- @cls = cls
9
+ def initialize(algorithm)
10
+ @algorithm = algorithm
11
+ end
12
+
13
+ def alg
14
+ return @algorithm.alg if @algorithm.respond_to?(:alg)
15
+
16
+ super
11
17
  end
12
18
 
13
19
  def valid_alg?(alg_to_check)
14
- alg&.casecmp(alg_to_check)&.zero? == true
20
+ return @algorithm.valid_alg?(alg_to_check) if @algorithm.respond_to?(:valid_alg?)
21
+
22
+ super
15
23
  end
16
24
 
17
- def sign(data:, signing_key:)
18
- cls.sign(alg, data, signing_key)
25
+ def header(*args, **kwargs)
26
+ return @algorithm.header(*args, **kwargs) if @algorithm.respond_to?(:header)
27
+
28
+ super
19
29
  end
20
30
 
21
- def verify(data:, signature:, verification_key:)
22
- cls.verify(alg, verification_key, data, signature)
31
+ def sign(*args, **kwargs)
32
+ return @algorithm.sign(*args, **kwargs) if @algorithm.respond_to?(:sign)
33
+
34
+ super
35
+ end
36
+
37
+ def verify(*args, **kwargs)
38
+ return @algorithm.verify(*args, **kwargs) if @algorithm.respond_to?(:verify)
39
+
40
+ super
23
41
  end
24
42
  end
25
43
  end
data/lib/jwt/jwa.rb CHANGED
@@ -8,54 +8,50 @@ rescue LoadError
8
8
  raise if defined?(RbNaCl)
9
9
  end
10
10
 
11
- require_relative 'jwa/hmac'
12
- require_relative 'jwa/eddsa'
11
+ require_relative 'jwa/compat'
12
+ require_relative 'jwa/signing_algorithm'
13
13
  require_relative 'jwa/ecdsa'
14
- require_relative 'jwa/rsa'
15
- require_relative 'jwa/ps'
14
+ require_relative 'jwa/hmac'
16
15
  require_relative 'jwa/none'
16
+ require_relative 'jwa/ps'
17
+ require_relative 'jwa/rsa'
17
18
  require_relative 'jwa/unsupported'
18
19
  require_relative 'jwa/wrapper'
19
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
+
20
29
  module JWT
30
+ # The JWA module contains all supported algorithms.
21
31
  module JWA
22
- ALGOS = [Hmac, Ecdsa, Rsa, Eddsa, Ps, None, Unsupported].tap do |l|
23
- if ::JWT.rbnacl_6_or_greater?
24
- require_relative 'jwa/hmac_rbnacl'
25
- l << Algos::HmacRbNaCl
26
- elsif ::JWT.rbnacl?
27
- require_relative 'jwa/hmac_rbnacl_fixed'
28
- l << Algos::HmacRbNaClFixed
29
- end
30
- end.freeze
31
-
32
32
  class << self
33
- def find(algorithm)
34
- indexed[algorithm&.downcase]
35
- end
33
+ # @api private
34
+ def resolve(algorithm)
35
+ return find(algorithm) if algorithm.is_a?(String) || algorithm.is_a?(Symbol)
36
36
 
37
- def create(algorithm)
38
- return algorithm if JWA.implementation?(algorithm)
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
39
41
 
40
- Wrapper.new(*find(algorithm))
42
+ algorithm
41
43
  end
42
44
 
43
- def implementation?(algorithm)
44
- (algorithm.respond_to?(:valid_alg?) && algorithm.respond_to?(:verify)) ||
45
- (algorithm.respond_to?(:alg) && algorithm.respond_to?(:sign))
45
+ # @api private
46
+ 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
46
49
  end
47
50
 
48
- private
49
-
50
- def indexed
51
- @indexed ||= begin
52
- fallback = [nil, Unsupported]
53
- ALGOS.each_with_object(Hash.new(fallback)) do |cls, hash|
54
- cls.const_get(:SUPPORTED).each do |alg|
55
- hash[alg.downcase] = [alg, cls]
56
- end
57
- end
58
- end
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)
59
55
  end
60
56
  end
61
57
  end