jwt 2.1.0 → 2.5.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 (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