jwt 2.1.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +6 -18
  3. data/.github/workflows/coverage.yml +27 -0
  4. data/.github/workflows/test.yml +67 -0
  5. data/.gitignore +3 -1
  6. data/.reek.yml +21 -39
  7. data/.rspec +1 -0
  8. data/.rubocop.yml +21 -52
  9. data/{.ebert.yml → .sourcelevel.yml} +3 -4
  10. data/AUTHORS +119 -0
  11. data/Appraisals +13 -0
  12. data/CHANGELOG.md +329 -19
  13. data/CODE_OF_CONDUCT.md +84 -0
  14. data/CONTRIBUTING.md +99 -0
  15. data/Gemfile +4 -0
  16. data/README.md +261 -100
  17. data/Rakefile +6 -1
  18. data/lib/jwt/algos/ecdsa.rb +37 -8
  19. data/lib/jwt/algos/eddsa.rb +16 -4
  20. data/lib/jwt/algos/hmac.rb +3 -0
  21. data/lib/jwt/algos/none.rb +17 -0
  22. data/lib/jwt/algos/ps.rb +43 -0
  23. data/lib/jwt/algos/rsa.rb +4 -1
  24. data/lib/jwt/algos/unsupported.rb +7 -4
  25. data/lib/jwt/algos.rb +44 -0
  26. data/lib/jwt/base64.rb +19 -0
  27. data/lib/jwt/claims_validator.rb +37 -0
  28. data/lib/jwt/configuration/container.rb +21 -0
  29. data/lib/jwt/configuration/decode_configuration.rb +46 -0
  30. data/lib/jwt/configuration/jwk_configuration.rb +27 -0
  31. data/lib/jwt/configuration.rb +15 -0
  32. data/lib/jwt/decode.rb +120 -24
  33. data/lib/jwt/encode.rb +43 -25
  34. data/lib/jwt/error.rb +6 -0
  35. data/lib/jwt/json.rb +18 -0
  36. data/lib/jwt/jwk/ec.rb +199 -0
  37. data/lib/jwt/jwk/hmac.rb +67 -0
  38. data/lib/jwt/jwk/key_base.rb +35 -0
  39. data/lib/jwt/jwk/key_finder.rb +62 -0
  40. data/lib/jwt/jwk/kid_as_key_digest.rb +15 -0
  41. data/lib/jwt/jwk/rsa.rb +138 -0
  42. data/lib/jwt/jwk/thumbprint.rb +26 -0
  43. data/lib/jwt/jwk.rb +52 -0
  44. data/lib/jwt/security_utils.rb +8 -0
  45. data/lib/jwt/signature.rb +7 -22
  46. data/lib/jwt/verify.rb +19 -8
  47. data/lib/jwt/version.rb +6 -2
  48. data/lib/jwt/x5c_key_finder.rb +55 -0
  49. data/lib/jwt.rb +12 -44
  50. data/ruby-jwt.gemspec +13 -9
  51. metadata +44 -97
  52. data/.travis.yml +0 -14
  53. data/Manifest +0 -8
  54. data/lib/jwt/default_options.rb +0 -15
  55. data/spec/fixtures/certs/ec256-private.pem +0 -8
  56. data/spec/fixtures/certs/ec256-public.pem +0 -4
  57. data/spec/fixtures/certs/ec256-wrong-private.pem +0 -8
  58. data/spec/fixtures/certs/ec256-wrong-public.pem +0 -4
  59. data/spec/fixtures/certs/ec384-private.pem +0 -9
  60. data/spec/fixtures/certs/ec384-public.pem +0 -5
  61. data/spec/fixtures/certs/ec384-wrong-private.pem +0 -9
  62. data/spec/fixtures/certs/ec384-wrong-public.pem +0 -5
  63. data/spec/fixtures/certs/ec512-private.pem +0 -10
  64. data/spec/fixtures/certs/ec512-public.pem +0 -6
  65. data/spec/fixtures/certs/ec512-wrong-private.pem +0 -10
  66. data/spec/fixtures/certs/ec512-wrong-public.pem +0 -6
  67. data/spec/fixtures/certs/rsa-1024-private.pem +0 -15
  68. data/spec/fixtures/certs/rsa-1024-public.pem +0 -6
  69. data/spec/fixtures/certs/rsa-2048-private.pem +0 -27
  70. data/spec/fixtures/certs/rsa-2048-public.pem +0 -9
  71. data/spec/fixtures/certs/rsa-2048-wrong-private.pem +0 -27
  72. data/spec/fixtures/certs/rsa-2048-wrong-public.pem +0 -9
  73. data/spec/fixtures/certs/rsa-4096-private.pem +0 -51
  74. data/spec/fixtures/certs/rsa-4096-public.pem +0 -14
  75. data/spec/integration/readme_examples_spec.rb +0 -202
  76. data/spec/jwt/verify_spec.rb +0 -232
  77. data/spec/jwt_spec.rb +0 -315
  78. data/spec/spec_helper.rb +0 -28
@@ -1,35 +1,64 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JWT
2
4
  module Algos
3
5
  module Ecdsa
4
6
  module_function
5
7
 
6
- SUPPORTED = %(ES256 ES384 ES512).freeze
7
8
  NAMED_CURVES = {
8
- 'prime256v1' => 'ES256',
9
- 'secp384r1' => 'ES384',
10
- 'secp521r1' => 'ES512'
9
+ 'prime256v1' => {
10
+ algorithm: 'ES256',
11
+ digest: 'sha256'
12
+ },
13
+ 'secp256r1' => { # alias for prime256v1
14
+ algorithm: 'ES256',
15
+ digest: 'sha256'
16
+ },
17
+ 'secp384r1' => {
18
+ algorithm: 'ES384',
19
+ digest: 'sha384'
20
+ },
21
+ 'secp521r1' => {
22
+ algorithm: 'ES512',
23
+ digest: 'sha512'
24
+ },
25
+ 'secp256k1' => {
26
+ algorithm: 'ES256K',
27
+ digest: 'sha256'
28
+ }
11
29
  }.freeze
12
30
 
31
+ SUPPORTED = NAMED_CURVES.map { |_, c| c[:algorithm] }.uniq.freeze
32
+
13
33
  def sign(to_sign)
14
34
  algorithm, msg, key = to_sign.values
15
- key_algorithm = NAMED_CURVES[key.group.curve_name]
35
+ curve_definition = curve_by_name(key.group.curve_name)
36
+ key_algorithm = curve_definition[:algorithm]
16
37
  if algorithm != key_algorithm
17
38
  raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
18
39
  end
19
40
 
20
- digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
41
+ digest = OpenSSL::Digest.new(curve_definition[:digest])
21
42
  SecurityUtils.asn1_to_raw(key.dsa_sign_asn1(digest.digest(msg)), key)
22
43
  end
23
44
 
24
45
  def verify(to_verify)
25
46
  algorithm, public_key, signing_input, signature = to_verify.values
26
- key_algorithm = NAMED_CURVES[public_key.group.curve_name]
47
+ curve_definition = curve_by_name(public_key.group.curve_name)
48
+ key_algorithm = curve_definition[:algorithm]
27
49
  if algorithm != key_algorithm
28
50
  raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
29
51
  end
30
- digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
52
+
53
+ digest = OpenSSL::Digest.new(curve_definition[:digest])
31
54
  public_key.dsa_verify_asn1(digest.digest(signing_input), SecurityUtils.raw_to_asn1(signature, public_key))
32
55
  end
56
+
57
+ def curve_by_name(name)
58
+ NAMED_CURVES.fetch(name) do
59
+ raise UnsupportedEcdsaCurve, "The ECDSA curve '#{name}' is not supported"
60
+ end
61
+ end
33
62
  end
34
63
  end
35
64
  end
@@ -1,22 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JWT
2
4
  module Algos
3
5
  module Eddsa
4
6
  module_function
5
7
 
6
- SUPPORTED = %w[ED25519].freeze
8
+ SUPPORTED = %w[ED25519 EdDSA].freeze
7
9
 
8
10
  def sign(to_sign)
9
11
  algorithm, msg, key = to_sign.values
10
- raise EncodeError, "Key given is a #{key.class} but has to be an RbNaCl::Signatures::Ed25519::SigningKey" if key.class != RbNaCl::Signatures::Ed25519::SigningKey
11
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key.primitive} signing key was provided" if algorithm.downcase.to_sym != key.primitive
12
+ if key.class != RbNaCl::Signatures::Ed25519::SigningKey
13
+ raise EncodeError, "Key given is a #{key.class} but has to be an RbNaCl::Signatures::Ed25519::SigningKey"
14
+ end
15
+ unless SUPPORTED.map(&:downcase).map(&:to_sym).include?(algorithm.downcase.to_sym)
16
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key.primitive} signing key was provided"
17
+ end
18
+
12
19
  key.sign(msg)
13
20
  end
14
21
 
15
22
  def verify(to_verify)
16
23
  algorithm, public_key, signing_input, signature = to_verify.values
17
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{public_key.primitive} verification key was provided" if algorithm.downcase.to_sym != public_key.primitive
24
+ unless SUPPORTED.map(&:downcase).map(&:to_sym).include?(algorithm.downcase.to_sym)
25
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key.primitive} signing key was provided"
26
+ end
18
27
  raise DecodeError, "key given is a #{public_key.class} but has to be a RbNaCl::Signatures::Ed25519::VerifyKey" if public_key.class != RbNaCl::Signatures::Ed25519::VerifyKey
28
+
19
29
  public_key.verify(signature, signing_input)
30
+ rescue RbNaCl::CryptoError
31
+ false
20
32
  end
21
33
  end
22
34
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JWT
2
4
  module Algos
3
5
  module Hmac
@@ -7,6 +9,7 @@ module JWT
7
9
 
8
10
  def sign(to_sign)
9
11
  algorithm, msg, key = to_sign.values
12
+ key ||= ''
10
13
  authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, key)
11
14
  if authenticator && padded_key
12
15
  authenticator.auth(padded_key, msg.encode('binary'))
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module None
6
+ module_function
7
+
8
+ SUPPORTED = %w[none].freeze
9
+
10
+ def sign(*); end
11
+
12
+ def verify(*)
13
+ true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Ps
6
+ # RSASSA-PSS signing algorithms
7
+
8
+ module_function
9
+
10
+ SUPPORTED = %w[PS256 PS384 PS512].freeze
11
+
12
+ def sign(to_sign)
13
+ require_openssl!
14
+
15
+ algorithm, msg, key = to_sign.values
16
+
17
+ key_class = key.class
18
+
19
+ raise EncodeError, "The given key is a #{key_class}. It has to be an OpenSSL::PKey::RSA instance." if key_class == String
20
+
21
+ translated_algorithm = algorithm.sub('PS', 'sha')
22
+
23
+ key.sign_pss(translated_algorithm, msg, salt_length: :digest, mgf1_hash: translated_algorithm)
24
+ end
25
+
26
+ def verify(to_verify)
27
+ require_openssl!
28
+
29
+ SecurityUtils.verify_ps(to_verify.algorithm, to_verify.public_key, to_verify.signing_input, to_verify.signature)
30
+ end
31
+
32
+ def require_openssl!
33
+ if Object.const_defined?('OpenSSL')
34
+ if ::Gem::Version.new(OpenSSL::VERSION) < ::Gem::Version.new('2.1')
35
+ raise JWT::RequiredDependencyError, "You currently have OpenSSL #{OpenSSL::VERSION}. PS support requires >= 2.1"
36
+ end
37
+ else
38
+ raise JWT::RequiredDependencyError, 'PS signing requires OpenSSL +2.1'
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
data/lib/jwt/algos/rsa.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JWT
2
4
  module Algos
3
5
  module Rsa
@@ -7,7 +9,8 @@ module JWT
7
9
 
8
10
  def sign(to_sign)
9
11
  algorithm, msg, key = to_sign.values
10
- raise EncodeError, "The given key is a #{key.class}. It has to be an OpenSSL::PKey::RSA instance." if key.class == String
12
+ raise EncodeError, "The given key is a #{key.class}. It has to be an OpenSSL::PKey::RSA instance." if key.instance_of?(String)
13
+
11
14
  key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
12
15
  end
13
16
 
@@ -1,16 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JWT
2
4
  module Algos
3
5
  module Unsupported
4
6
  module_function
5
7
 
6
- SUPPORTED = Object.new.tap { |object| object.define_singleton_method(:include?) { |*| true } }
7
- def verify(*)
8
- raise JWT::VerificationError, 'Algorithm not supported'
9
- end
8
+ SUPPORTED = [].freeze
10
9
 
11
10
  def sign(*)
12
11
  raise NotImplementedError, 'Unsupported signing method'
13
12
  end
13
+
14
+ def verify(*)
15
+ raise JWT::VerificationError, 'Algorithm not supported'
16
+ end
14
17
  end
15
18
  end
16
19
  end
data/lib/jwt/algos.rb ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jwt/algos/hmac'
4
+ require 'jwt/algos/eddsa'
5
+ require 'jwt/algos/ecdsa'
6
+ require 'jwt/algos/rsa'
7
+ require 'jwt/algos/ps'
8
+ require 'jwt/algos/none'
9
+ require 'jwt/algos/unsupported'
10
+
11
+ # JWT::Signature module
12
+ module JWT
13
+ # Signature logic for JWT
14
+ module Algos
15
+ extend self
16
+
17
+ ALGOS = [
18
+ Algos::Hmac,
19
+ Algos::Ecdsa,
20
+ Algos::Rsa,
21
+ Algos::Eddsa,
22
+ Algos::Ps,
23
+ Algos::None,
24
+ Algos::Unsupported
25
+ ].freeze
26
+
27
+ def find(algorithm)
28
+ indexed[algorithm && algorithm.downcase]
29
+ end
30
+
31
+ private
32
+
33
+ def indexed
34
+ @indexed ||= begin
35
+ fallback = [Algos::Unsupported, nil]
36
+ ALGOS.each_with_object(Hash.new(fallback)) do |alg, hash|
37
+ alg.const_get(:SUPPORTED).each do |code|
38
+ hash[code.downcase] = [alg, code]
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
data/lib/jwt/base64.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module JWT
6
+ # Base64 helpers
7
+ class Base64
8
+ class << self
9
+ def url_encode(str)
10
+ ::Base64.encode64(str).tr('+/', '-_').gsub(/[\n=]/, '')
11
+ end
12
+
13
+ def url_decode(str)
14
+ str += '=' * (4 - str.length.modulo(4))
15
+ ::Base64.decode64(str.tr('-_', '+/'))
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './error'
4
+
5
+ module JWT
6
+ class ClaimsValidator
7
+ NUMERIC_CLAIMS = %i[
8
+ exp
9
+ iat
10
+ nbf
11
+ ].freeze
12
+
13
+ def initialize(payload)
14
+ @payload = payload.transform_keys(&:to_sym)
15
+ end
16
+
17
+ def validate!
18
+ validate_numeric_claims
19
+
20
+ true
21
+ end
22
+
23
+ private
24
+
25
+ def validate_numeric_claims
26
+ NUMERIC_CLAIMS.each do |claim|
27
+ validate_is_numeric(claim) if @payload.key?(claim)
28
+ end
29
+ end
30
+
31
+ def validate_is_numeric(claim)
32
+ return if @payload[claim].is_a?(Numeric)
33
+
34
+ raise InvalidPayload, "#{claim} claim must be a Numeric value but it is a #{@payload[claim].class}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'decode_configuration'
4
+ require_relative 'jwk_configuration'
5
+
6
+ module JWT
7
+ module Configuration
8
+ class Container
9
+ attr_accessor :decode, :jwk
10
+
11
+ def initialize
12
+ reset!
13
+ end
14
+
15
+ def reset!
16
+ @decode = DecodeConfiguration.new
17
+ @jwk = JwkConfiguration.new
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Configuration
5
+ class DecodeConfiguration
6
+ attr_accessor :verify_expiration,
7
+ :verify_not_before,
8
+ :verify_iss,
9
+ :verify_iat,
10
+ :verify_jti,
11
+ :verify_aud,
12
+ :verify_sub,
13
+ :leeway,
14
+ :algorithms,
15
+ :required_claims
16
+
17
+ def initialize
18
+ @verify_expiration = true
19
+ @verify_not_before = true
20
+ @verify_iss = false
21
+ @verify_iat = false
22
+ @verify_jti = false
23
+ @verify_aud = false
24
+ @verify_sub = false
25
+ @leeway = 0
26
+ @algorithms = ['HS256']
27
+ @required_claims = []
28
+ end
29
+
30
+ def to_h
31
+ {
32
+ verify_expiration: verify_expiration,
33
+ verify_not_before: verify_not_before,
34
+ verify_iss: verify_iss,
35
+ verify_iat: verify_iat,
36
+ verify_jti: verify_jti,
37
+ verify_aud: verify_aud,
38
+ verify_sub: verify_sub,
39
+ leeway: leeway,
40
+ algorithms: algorithms,
41
+ required_claims: required_claims
42
+ }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../jwk/kid_as_key_digest'
4
+ require_relative '../jwk/thumbprint'
5
+
6
+ module JWT
7
+ module Configuration
8
+ class JwkConfiguration
9
+ def initialize
10
+ self.kid_generator_type = :key_digest
11
+ end
12
+
13
+ def kid_generator_type=(value)
14
+ self.kid_generator = case value
15
+ when :key_digest
16
+ JWT::JWK::KidAsKeyDigest
17
+ when :rfc7638_thumbprint
18
+ JWT::JWK::Thumbprint
19
+ else
20
+ raise ArgumentError, "#{value} is not a valid kid generator type."
21
+ end
22
+ end
23
+
24
+ attr_accessor :kid_generator
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configuration/container'
4
+
5
+ module JWT
6
+ module Configuration
7
+ def configure
8
+ yield(configuration)
9
+ end
10
+
11
+ def configuration
12
+ @configuration ||= ::JWT::Configuration::Container.new
13
+ end
14
+ end
15
+ end
data/lib/jwt/decode.rb CHANGED
@@ -2,47 +2,143 @@
2
2
 
3
3
  require 'json'
4
4
 
5
+ require 'jwt/signature'
6
+ require 'jwt/verify'
7
+ require 'jwt/x5c_key_finder'
5
8
  # JWT::Decode module
6
9
  module JWT
7
10
  # Decoding logic for JWT
8
11
  class Decode
9
- attr_reader :header, :payload, :signature
12
+ def initialize(jwt, key, verify, options, &keyfinder)
13
+ raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
10
14
 
11
- def self.base64url_decode(str)
12
- str += '=' * (4 - str.length.modulo(4))
13
- Base64.decode64(str.tr('-_', '+/'))
14
- end
15
-
16
- def initialize(jwt, verify)
17
15
  @jwt = jwt
16
+ @key = key
17
+ @options = options
18
+ @segments = jwt.split('.')
18
19
  @verify = verify
19
- @header = ''
20
- @payload = ''
21
20
  @signature = ''
21
+ @keyfinder = keyfinder
22
22
  end
23
23
 
24
24
  def decode_segments
25
- header_segment, payload_segment, crypto_segment = raw_segments
26
- @header, @payload = decode_header_and_payload(header_segment, payload_segment)
27
- @signature = Decode.base64url_decode(crypto_segment.to_s) if @verify
28
- signing_input = [header_segment, payload_segment].join('.')
29
- [@header, @payload, @signature, signing_input]
25
+ validate_segment_count!
26
+ if @verify
27
+ decode_crypto
28
+ verify_algo
29
+ set_key
30
+ verify_signature
31
+ verify_claims
32
+ end
33
+ raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
34
+
35
+ [payload, header]
30
36
  end
31
37
 
32
38
  private
33
39
 
34
- def raw_segments
35
- segments = @jwt.split('.')
36
- required_num_segments = @verify ? [3] : [2, 3]
37
- raise(JWT::DecodeError, 'Not enough or too many segments') unless required_num_segments.include? segments.length
38
- segments
40
+ def verify_signature
41
+ return unless @key || @verify
42
+
43
+ return if none_algorithm?
44
+
45
+ raise JWT::DecodeError, 'No verification key available' unless @key
46
+
47
+ return if Array(@key).any? { |key| verify_signature_for?(key) }
48
+
49
+ raise(JWT::VerificationError, 'Signature verification failed')
50
+ end
51
+
52
+ def verify_algo
53
+ raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') if allowed_algorithms.empty?
54
+ raise(JWT::IncorrectAlgorithm, 'Token is missing alg header') unless algorithm
55
+ raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless options_includes_algo_in_header?
56
+ end
57
+
58
+ def set_key
59
+ @key = find_key(&@keyfinder) if @keyfinder
60
+ @key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks]).key_for(header['kid']) if @options[:jwks]
61
+ if (x5c_options = @options[:x5c])
62
+ @key = X5cKeyFinder.new(x5c_options[:root_certificates], x5c_options[:crls]).from(header['x5c'])
63
+ end
64
+ end
65
+
66
+ def verify_signature_for?(key)
67
+ Signature.verify(algorithm, key, signing_input, @signature)
68
+ end
69
+
70
+ def options_includes_algo_in_header?
71
+ allowed_algorithms.any? { |alg| alg.casecmp(algorithm).zero? }
72
+ end
73
+
74
+ def allowed_algorithms
75
+ # Order is very important - first check for string keys, next for symbols
76
+ algos = if @options.key?('algorithm')
77
+ @options['algorithm']
78
+ elsif @options.key?(:algorithm)
79
+ @options[:algorithm]
80
+ elsif @options.key?('algorithms')
81
+ @options['algorithms']
82
+ elsif @options.key?(:algorithms)
83
+ @options[:algorithms]
84
+ else
85
+ []
86
+ end
87
+ Array(algos)
88
+ end
89
+
90
+ def find_key(&keyfinder)
91
+ key = (keyfinder.arity == 2 ? yield(header, payload) : yield(header))
92
+ # key can be of type [string, nil, OpenSSL::PKey, Array]
93
+ return key if key && !Array(key).empty?
94
+
95
+ raise JWT::DecodeError, 'No verification key available'
96
+ end
97
+
98
+ def verify_claims
99
+ Verify.verify_claims(payload, @options)
100
+ Verify.verify_required_claims(payload, @options)
101
+ end
102
+
103
+ def validate_segment_count!
104
+ return if segment_length == 3
105
+ return if !@verify && segment_length == 2 # If no verifying required, the signature is not needed
106
+ return if segment_length == 2 && none_algorithm?
107
+
108
+ raise(JWT::DecodeError, 'Not enough or too many segments')
109
+ end
110
+
111
+ def segment_length
112
+ @segments.count
113
+ end
114
+
115
+ def none_algorithm?
116
+ algorithm == 'none'
117
+ end
118
+
119
+ def decode_crypto
120
+ @signature = ::JWT::Base64.url_decode(@segments[2] || '')
121
+ end
122
+
123
+ def algorithm
124
+ header['alg']
125
+ end
126
+
127
+ def header
128
+ @header ||= parse_and_decode @segments[0]
129
+ end
130
+
131
+ def payload
132
+ @payload ||= parse_and_decode @segments[1]
133
+ end
134
+
135
+ def signing_input
136
+ @segments.first(2).join('.')
39
137
  end
40
138
 
41
- def decode_header_and_payload(header_segment, payload_segment)
42
- header = JSON.parse(Decode.base64url_decode(header_segment))
43
- payload = JSON.parse(Decode.base64url_decode(payload_segment))
44
- [header, payload]
45
- rescue JSON::ParserError
139
+ def parse_and_decode(segment)
140
+ JWT::JSON.parse(::JWT::Base64.url_decode(segment))
141
+ rescue ::JSON::ParserError
46
142
  raise JWT::DecodeError, 'Invalid segment encoding'
47
143
  end
48
144
  end