jwt 1.5.4 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +5 -13
  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 +7 -0
  6. data/.reek.yml +22 -0
  7. data/.rspec +1 -1
  8. data/.rubocop.yml +66 -1
  9. data/.sourcelevel.yml +17 -0
  10. data/AUTHORS +119 -0
  11. data/Appraisals +13 -0
  12. data/CHANGELOG.md +786 -0
  13. data/CODE_OF_CONDUCT.md +84 -0
  14. data/CONTRIBUTING.md +99 -0
  15. data/Gemfile +4 -1
  16. data/README.md +332 -79
  17. data/Rakefile +15 -0
  18. data/lib/jwt/algos/ecdsa.rb +64 -0
  19. data/lib/jwt/algos/eddsa.rb +35 -0
  20. data/lib/jwt/algos/hmac.rb +36 -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 +22 -0
  24. data/lib/jwt/algos/unsupported.rb +19 -0
  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 +119 -30
  33. data/lib/jwt/encode.rb +69 -0
  34. data/lib/jwt/error.rb +10 -0
  35. data/lib/jwt/json.rb +11 -9
  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 +59 -0
  45. data/lib/jwt/signature.rb +35 -0
  46. data/lib/jwt/verify.rb +59 -44
  47. data/lib/jwt/version.rb +8 -3
  48. data/lib/jwt/x5c_key_finder.rb +55 -0
  49. data/lib/jwt.rb +16 -162
  50. data/ruby-jwt.gemspec +14 -8
  51. metadata +71 -84
  52. data/.travis.yml +0 -13
  53. data/Manifest +0 -8
  54. data/spec/fixtures/certs/ec256-private.pem +0 -8
  55. data/spec/fixtures/certs/ec256-public.pem +0 -4
  56. data/spec/fixtures/certs/ec256-wrong-private.pem +0 -8
  57. data/spec/fixtures/certs/ec256-wrong-public.pem +0 -4
  58. data/spec/fixtures/certs/ec384-private.pem +0 -9
  59. data/spec/fixtures/certs/ec384-public.pem +0 -5
  60. data/spec/fixtures/certs/ec384-wrong-private.pem +0 -9
  61. data/spec/fixtures/certs/ec384-wrong-public.pem +0 -5
  62. data/spec/fixtures/certs/ec512-private.pem +0 -10
  63. data/spec/fixtures/certs/ec512-public.pem +0 -6
  64. data/spec/fixtures/certs/ec512-wrong-private.pem +0 -10
  65. data/spec/fixtures/certs/ec512-wrong-public.pem +0 -6
  66. data/spec/fixtures/certs/rsa-1024-private.pem +0 -15
  67. data/spec/fixtures/certs/rsa-1024-public.pem +0 -6
  68. data/spec/fixtures/certs/rsa-2048-private.pem +0 -27
  69. data/spec/fixtures/certs/rsa-2048-public.pem +0 -9
  70. data/spec/fixtures/certs/rsa-2048-wrong-private.pem +0 -27
  71. data/spec/fixtures/certs/rsa-2048-wrong-public.pem +0 -9
  72. data/spec/fixtures/certs/rsa-4096-private.pem +0 -51
  73. data/spec/fixtures/certs/rsa-4096-public.pem +0 -14
  74. data/spec/jwt/verify_spec.rb +0 -175
  75. data/spec/jwt_spec.rb +0 -232
  76. data/spec/spec_helper.rb +0 -31
data/Rakefile CHANGED
@@ -1 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
1
4
  require 'bundler/gem_tasks'
5
+
6
+ begin
7
+ require 'rspec/core/rake_task'
8
+ require 'rubocop/rake_task'
9
+
10
+ RSpec::Core::RakeTask.new(:test)
11
+ RuboCop::RakeTask.new(:rubocop)
12
+
13
+ task default: %i[rubocop test]
14
+ rescue LoadError
15
+ puts 'RSpec rake tasks not available. Please run "bundle install" to install missing dependencies.'
16
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Ecdsa
6
+ module_function
7
+
8
+ NAMED_CURVES = {
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
+ }
29
+ }.freeze
30
+
31
+ SUPPORTED = NAMED_CURVES.map { |_, c| c[:algorithm] }.uniq.freeze
32
+
33
+ def sign(to_sign)
34
+ algorithm, msg, key = to_sign.values
35
+ curve_definition = curve_by_name(key.group.curve_name)
36
+ key_algorithm = curve_definition[:algorithm]
37
+ if algorithm != key_algorithm
38
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
39
+ end
40
+
41
+ digest = OpenSSL::Digest.new(curve_definition[:digest])
42
+ SecurityUtils.asn1_to_raw(key.dsa_sign_asn1(digest.digest(msg)), key)
43
+ end
44
+
45
+ def verify(to_verify)
46
+ algorithm, public_key, signing_input, signature = to_verify.values
47
+ curve_definition = curve_by_name(public_key.group.curve_name)
48
+ key_algorithm = curve_definition[:algorithm]
49
+ if algorithm != key_algorithm
50
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
51
+ end
52
+
53
+ digest = OpenSSL::Digest.new(curve_definition[:digest])
54
+ public_key.dsa_verify_asn1(digest.digest(signing_input), SecurityUtils.raw_to_asn1(signature, public_key))
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
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Eddsa
6
+ module_function
7
+
8
+ SUPPORTED = %w[ED25519 EdDSA].freeze
9
+
10
+ def sign(to_sign)
11
+ algorithm, msg, key = to_sign.values
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
+
19
+ key.sign(msg)
20
+ end
21
+
22
+ def verify(to_verify)
23
+ algorithm, public_key, signing_input, signature = to_verify.values
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
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
+
29
+ public_key.verify(signature, signing_input)
30
+ rescue RbNaCl::CryptoError
31
+ false
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Hmac
6
+ module_function
7
+
8
+ SUPPORTED = %w[HS256 HS512256 HS384 HS512].freeze
9
+
10
+ def sign(to_sign)
11
+ algorithm, msg, key = to_sign.values
12
+ key ||= ''
13
+ authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, key)
14
+ if authenticator && padded_key
15
+ authenticator.auth(padded_key, msg.encode('binary'))
16
+ else
17
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
18
+ end
19
+ end
20
+
21
+ def verify(to_verify)
22
+ algorithm, public_key, signing_input, signature = to_verify.values
23
+ authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, public_key)
24
+ if authenticator && padded_key
25
+ begin
26
+ authenticator.verify(padded_key, signature.encode('binary'), signing_input.encode('binary'))
27
+ rescue RbNaCl::BadAuthenticatorError
28
+ false
29
+ end
30
+ else
31
+ SecurityUtils.secure_compare(signature, sign(JWT::Signature::ToSign.new(algorithm, signing_input, public_key)))
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -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
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Rsa
6
+ module_function
7
+
8
+ SUPPORTED = %w[RS256 RS384 RS512].freeze
9
+
10
+ def sign(to_sign)
11
+ algorithm, msg, key = to_sign.values
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
+
14
+ key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
15
+ end
16
+
17
+ def verify(to_verify)
18
+ SecurityUtils.verify_rsa(to_verify.algorithm, to_verify.public_key, to_verify.signing_input, to_verify.signature)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Unsupported
6
+ module_function
7
+
8
+ SUPPORTED = [].freeze
9
+
10
+ def sign(*)
11
+ raise NotImplementedError, 'Unsupported signing method'
12
+ end
13
+
14
+ def verify(*)
15
+ raise JWT::VerificationError, 'Algorithm not supported'
16
+ end
17
+ end
18
+ end
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
@@ -1,56 +1,145 @@
1
- require 'jwt/json'
2
- require 'jwt/verify'
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
3
4
 
5
+ require 'jwt/signature'
6
+ require 'jwt/verify'
7
+ require 'jwt/x5c_key_finder'
4
8
  # JWT::Decode module
5
9
  module JWT
6
- extend JWT::Json
7
-
8
10
  # Decoding logic for JWT
9
11
  class Decode
10
- attr_reader :header, :payload, :signature
11
-
12
12
  def initialize(jwt, key, verify, options, &keyfinder)
13
+ raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
14
+
13
15
  @jwt = jwt
14
16
  @key = key
15
- @verify = verify
16
17
  @options = options
18
+ @segments = jwt.split('.')
19
+ @verify = verify
20
+ @signature = ''
17
21
  @keyfinder = keyfinder
18
22
  end
19
23
 
20
24
  def decode_segments
21
- header_segment, payload_segment, crypto_segment = raw_segments(@jwt, @verify)
22
- @header, @payload = decode_header_and_payload(header_segment, payload_segment)
23
- @signature = Decode.base64url_decode(crypto_segment.to_s) if @verify
24
- signing_input = [header_segment, payload_segment].join('.')
25
- [@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]
36
+ end
37
+
38
+ private
39
+
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')
26
50
  end
27
51
 
28
- def raw_segments(jwt, verify)
29
- segments = jwt.split('.')
30
- required_num_segments = verify ? [3] : [2, 3]
31
- fail(JWT::DecodeError, 'Not enough or too many segments') unless required_num_segments.include? segments.length
32
- segments
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?
33
56
  end
34
- private :raw_segments
35
57
 
36
- def decode_header_and_payload(header_segment, payload_segment)
37
- header = JWT.decode_json(Decode.base64url_decode(header_segment))
38
- payload = JWT.decode_json(Decode.base64url_decode(payload_segment))
39
- [header, payload]
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
40
64
  end
41
- private :decode_header_and_payload
42
65
 
43
- def self.base64url_decode(str)
44
- str += '=' * (4 - str.length.modulo(4))
45
- Base64.decode64(str.tr('-_', '+/'))
66
+ def verify_signature_for?(key)
67
+ Signature.verify(algorithm, key, signing_input, @signature)
46
68
  end
47
69
 
48
- def verify
49
- @options.each do |key, val|
50
- next unless key.to_s.match(/verify/)
70
+ def options_includes_algo_in_header?
71
+ allowed_algorithms.any? { |alg| alg.casecmp(algorithm).zero? }
72
+ end
51
73
 
52
- Verify.send(key, payload, @options) if val
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
+ []
53
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('.')
137
+ end
138
+
139
+ def parse_and_decode(segment)
140
+ JWT::JSON.parse(::JWT::Base64.url_decode(segment))
141
+ rescue ::JSON::ParserError
142
+ raise JWT::DecodeError, 'Invalid segment encoding'
54
143
  end
55
144
  end
56
145
  end