jwt 1.5.6 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +5 -5
  2. data/.ebert.yml +18 -0
  3. data/.gitignore +1 -1
  4. data/.rubocop.yml +96 -0
  5. data/.travis.yml +26 -10
  6. data/AUTHORS +84 -0
  7. data/Appraisals +18 -0
  8. data/CHANGELOG.md +296 -10
  9. data/Gemfile +0 -1
  10. data/README.md +182 -64
  11. data/lib/jwt.rb +14 -176
  12. data/lib/jwt/algos/ecdsa.rb +35 -0
  13. data/lib/jwt/algos/eddsa.rb +23 -0
  14. data/lib/jwt/algos/hmac.rb +34 -0
  15. data/lib/jwt/algos/ps.rb +43 -0
  16. data/lib/jwt/algos/rsa.rb +19 -0
  17. data/lib/jwt/algos/unsupported.rb +16 -0
  18. data/lib/jwt/base64.rb +19 -0
  19. data/lib/jwt/claims_validator.rb +33 -0
  20. data/lib/jwt/decode.rb +81 -31
  21. data/lib/jwt/default_options.rb +15 -0
  22. data/lib/jwt/encode.rb +68 -0
  23. data/lib/jwt/error.rb +6 -0
  24. data/lib/jwt/json.rb +10 -9
  25. data/lib/jwt/jwk.rb +31 -0
  26. data/lib/jwt/jwk/key_finder.rb +57 -0
  27. data/lib/jwt/jwk/rsa.rb +54 -0
  28. data/lib/jwt/security_utils.rb +57 -0
  29. data/lib/jwt/signature.rb +54 -0
  30. data/lib/jwt/verify.rb +45 -53
  31. data/lib/jwt/version.rb +3 -3
  32. data/ruby-jwt.gemspec +11 -7
  33. metadata +76 -67
  34. data/Manifest +0 -8
  35. data/spec/fixtures/certs/ec256-private.pem +0 -8
  36. data/spec/fixtures/certs/ec256-public.pem +0 -4
  37. data/spec/fixtures/certs/ec256-wrong-private.pem +0 -8
  38. data/spec/fixtures/certs/ec256-wrong-public.pem +0 -4
  39. data/spec/fixtures/certs/ec384-private.pem +0 -9
  40. data/spec/fixtures/certs/ec384-public.pem +0 -5
  41. data/spec/fixtures/certs/ec384-wrong-private.pem +0 -9
  42. data/spec/fixtures/certs/ec384-wrong-public.pem +0 -5
  43. data/spec/fixtures/certs/ec512-private.pem +0 -10
  44. data/spec/fixtures/certs/ec512-public.pem +0 -6
  45. data/spec/fixtures/certs/ec512-wrong-private.pem +0 -10
  46. data/spec/fixtures/certs/ec512-wrong-public.pem +0 -6
  47. data/spec/fixtures/certs/rsa-1024-private.pem +0 -15
  48. data/spec/fixtures/certs/rsa-1024-public.pem +0 -6
  49. data/spec/fixtures/certs/rsa-2048-private.pem +0 -27
  50. data/spec/fixtures/certs/rsa-2048-public.pem +0 -9
  51. data/spec/fixtures/certs/rsa-2048-wrong-private.pem +0 -27
  52. data/spec/fixtures/certs/rsa-2048-wrong-public.pem +0 -9
  53. data/spec/fixtures/certs/rsa-4096-private.pem +0 -51
  54. data/spec/fixtures/certs/rsa-4096-public.pem +0 -14
  55. data/spec/integration/readme_examples_spec.rb +0 -190
  56. data/spec/jwt/verify_spec.rb +0 -197
  57. data/spec/jwt_spec.rb +0 -240
  58. data/spec/spec_helper.rb +0 -31
data/lib/jwt.rb CHANGED
@@ -1,192 +1,30 @@
1
1
  # frozen_string_literal: true
2
- require 'base64'
3
- require 'openssl'
2
+
3
+ require 'jwt/base64'
4
+ require 'jwt/json'
4
5
  require 'jwt/decode'
6
+ require 'jwt/default_options'
7
+ require 'jwt/encode'
5
8
  require 'jwt/error'
6
- require 'jwt/json'
9
+ require 'jwt/jwk'
7
10
 
8
11
  # JSON Web Token implementation
9
12
  #
10
13
  # Should be up to date with the latest spec:
11
- # https://tools.ietf.org/html/rfc7519#section-4.1.5
14
+ # https://tools.ietf.org/html/rfc7519
12
15
  module JWT
13
- extend JWT::Json
14
-
15
- NAMED_CURVES = {
16
- 'prime256v1' => 'ES256',
17
- 'secp384r1' => 'ES384',
18
- 'secp521r1' => 'ES512'
19
- }.freeze
20
-
21
- DEFAULT_OPTIONS = {
22
- verify_expiration: true,
23
- verify_not_before: true,
24
- verify_iss: false,
25
- verify_iat: false,
26
- verify_jti: false,
27
- verify_aud: false,
28
- verify_sub: false,
29
- leeway: 0
30
- }.freeze
16
+ include JWT::DefaultOptions
31
17
 
32
18
  module_function
33
19
 
34
- def sign(algorithm, msg, key)
35
- if %w(HS256 HS384 HS512).include?(algorithm)
36
- sign_hmac(algorithm, msg, key)
37
- elsif %w(RS256 RS384 RS512).include?(algorithm)
38
- sign_rsa(algorithm, msg, key)
39
- elsif %w(ES256 ES384 ES512).include?(algorithm)
40
- sign_ecdsa(algorithm, msg, key)
41
- else
42
- raise NotImplementedError, 'Unsupported signing method'
43
- end
44
- end
45
-
46
- def sign_rsa(algorithm, msg, private_key)
47
- private_key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
48
- end
49
-
50
- def sign_ecdsa(algorithm, msg, private_key)
51
- key_algorithm = NAMED_CURVES[private_key.group.curve_name]
52
- if algorithm != key_algorithm
53
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
54
- end
55
-
56
- digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
57
- asn1_to_raw(private_key.dsa_sign_asn1(digest.digest(msg)), private_key)
58
- end
59
-
60
- def verify_rsa(algorithm, public_key, signing_input, signature)
61
- public_key.verify(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
62
- end
63
-
64
- def verify_ecdsa(algorithm, public_key, signing_input, signature)
65
- key_algorithm = NAMED_CURVES[public_key.group.curve_name]
66
- if algorithm != key_algorithm
67
- raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
68
- end
69
-
70
- digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
71
- public_key.dsa_verify_asn1(digest.digest(signing_input), raw_to_asn1(signature, public_key))
72
- end
73
-
74
- def sign_hmac(algorithm, msg, key)
75
- OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
76
- end
77
-
78
- def base64url_encode(str)
79
- Base64.encode64(str).tr('+/', '-_').gsub(/[\n=]/, '')
80
- end
81
-
82
- def encoded_header(algorithm = 'HS256', header_fields = {})
83
- header = { 'typ' => 'JWT', 'alg' => algorithm }.merge(header_fields)
84
- base64url_encode(encode_json(header))
85
- end
86
-
87
- def encoded_payload(payload)
88
- raise InvalidPayload, 'exp claim must be an integer' if payload['exp'] && payload['exp'].is_a?(Time)
89
- base64url_encode(encode_json(payload))
90
- end
91
-
92
- def encoded_signature(signing_input, key, algorithm)
93
- if algorithm == 'none'
94
- ''
95
- else
96
- signature = sign(algorithm, signing_input, key)
97
- base64url_encode(signature)
98
- end
99
- end
100
-
101
20
  def encode(payload, key, algorithm = 'HS256', header_fields = {})
102
- algorithm ||= 'none'
103
- segments = []
104
- segments << encoded_header(algorithm, header_fields)
105
- segments << encoded_payload(payload)
106
- segments << encoded_signature(segments.join('.'), key, algorithm)
107
- segments.join('.')
108
- end
109
-
110
- def decoded_segments(jwt, key = nil, verify = true, custom_options = {}, &keyfinder)
111
- raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
112
-
113
- merged_options = DEFAULT_OPTIONS.merge(custom_options)
114
-
115
- decoder = Decode.new jwt, key, verify, merged_options, &keyfinder
116
- decoder.decode_segments
117
- end
118
-
119
- def decode(jwt, key = nil, verify = true, custom_options = {}, &keyfinder)
120
- raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
121
-
122
- merged_options = DEFAULT_OPTIONS.merge(custom_options)
123
- decoder = Decode.new jwt, key, verify, merged_options, &keyfinder
124
- header, payload, signature, signing_input = decoder.decode_segments
125
- decode_verify_signature(key, header, signature, signing_input, merged_options, &keyfinder) if verify
126
- decoder.verify
127
-
128
- raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
129
-
130
- [payload, header]
131
- end
132
-
133
- def decode_verify_signature(key, header, signature, signing_input, options, &keyfinder)
134
- algo, key = signature_algorithm_and_key(header, key, &keyfinder)
135
- if options[:algorithm] && algo != options[:algorithm]
136
- raise JWT::IncorrectAlgorithm, 'Expected a different algorithm'
137
- end
138
- verify_signature(algo, key, signing_input, signature)
139
- end
140
-
141
- def signature_algorithm_and_key(header, key, &keyfinder)
142
- key = yield(header) if keyfinder
143
- [header['alg'], key]
144
- end
145
-
146
- def verify_signature(algo, key, signing_input, signature)
147
- verify_signature_algo(algo, key, signing_input, signature)
148
- rescue OpenSSL::PKey::PKeyError
149
- raise JWT::VerificationError, 'Signature verification raised'
150
- ensure
151
- OpenSSL.errors.clear
152
- end
153
-
154
- def verify_signature_algo(algo, key, signing_input, signature)
155
- if %w(HS256 HS384 HS512).include?(algo)
156
- raise(JWT::VerificationError, 'Signature verification raised') unless secure_compare(signature, sign_hmac(algo, signing_input, key))
157
- elsif %w(RS256 RS384 RS512).include?(algo)
158
- raise(JWT::VerificationError, 'Signature verification raised') unless verify_rsa(algo, key, signing_input, signature)
159
- elsif %w(ES256 ES384 ES512).include?(algo)
160
- raise(JWT::VerificationError, 'Signature verification raised') unless verify_ecdsa(algo, key, signing_input, signature)
161
- else
162
- raise JWT::VerificationError, 'Algorithm not supported'
163
- end
164
- end
165
-
166
- # From devise
167
- # constant-time comparison algorithm to prevent timing attacks
168
- def secure_compare(a, b)
169
- return false if a.nil? || b.nil? || a.empty? || b.empty? || a.bytesize != b.bytesize
170
- l = a.unpack "C#{a.bytesize}"
171
-
172
- res = 0
173
- b.each_byte { |byte| res |= byte ^ l.shift }
174
- res.zero?
175
- end
176
-
177
- def raw_to_asn1(signature, private_key)
178
- byte_size = (private_key.group.degree + 7) / 8
179
- r = signature[0..(byte_size - 1)]
180
- s = signature[byte_size..-1]
181
- OpenSSL::ASN1::Sequence.new([r, s].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
182
- end
183
-
184
- def asn1_to_raw(signature, public_key)
185
- byte_size = (public_key.group.degree + 7) / 8
186
- OpenSSL::ASN1.decode(signature).value.map { |value| value.value.to_s(2).rjust(byte_size, "\x00") }.join
21
+ Encode.new(payload: payload,
22
+ key: key,
23
+ algorithm: algorithm,
24
+ headers: header_fields).segments
187
25
  end
188
26
 
189
- def base64url_decode(str)
190
- Decode.base64url_decode(str)
27
+ def decode(jwt, key = nil, verify = true, options = {}, &keyfinder)
28
+ Decode.new(jwt, key, verify, DEFAULT_OPTIONS.merge(options), &keyfinder).decode_segments
191
29
  end
192
30
  end
@@ -0,0 +1,35 @@
1
+ module JWT
2
+ module Algos
3
+ module Ecdsa
4
+ module_function
5
+
6
+ SUPPORTED = %w[ES256 ES384 ES512].freeze
7
+ NAMED_CURVES = {
8
+ 'prime256v1' => 'ES256',
9
+ 'secp384r1' => 'ES384',
10
+ 'secp521r1' => 'ES512'
11
+ }.freeze
12
+
13
+ def sign(to_sign)
14
+ algorithm, msg, key = to_sign.values
15
+ key_algorithm = NAMED_CURVES[key.group.curve_name]
16
+ if algorithm != key_algorithm
17
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
18
+ end
19
+
20
+ digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
21
+ SecurityUtils.asn1_to_raw(key.dsa_sign_asn1(digest.digest(msg)), key)
22
+ end
23
+
24
+ def verify(to_verify)
25
+ algorithm, public_key, signing_input, signature = to_verify.values
26
+ key_algorithm = NAMED_CURVES[public_key.group.curve_name]
27
+ if algorithm != key_algorithm
28
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
29
+ end
30
+ digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
31
+ public_key.dsa_verify_asn1(digest.digest(signing_input), SecurityUtils.raw_to_asn1(signature, public_key))
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ module JWT
2
+ module Algos
3
+ module Eddsa
4
+ module_function
5
+
6
+ SUPPORTED = %w[ED25519].freeze
7
+
8
+ def sign(to_sign)
9
+ 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
+ key.sign(msg)
13
+ end
14
+
15
+ def verify(to_verify)
16
+ 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
18
+ 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
19
+ public_key.verify(signature, signing_input)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ module JWT
2
+ module Algos
3
+ module Hmac
4
+ module_function
5
+
6
+ SUPPORTED = %w[HS256 HS512256 HS384 HS512].freeze
7
+
8
+ def sign(to_sign)
9
+ algorithm, msg, key = to_sign.values
10
+ key ||= ''
11
+ authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, key)
12
+ if authenticator && padded_key
13
+ authenticator.auth(padded_key, msg.encode('binary'))
14
+ else
15
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
16
+ end
17
+ end
18
+
19
+ def verify(to_verify)
20
+ algorithm, public_key, signing_input, signature = to_verify.values
21
+ authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, public_key)
22
+ if authenticator && padded_key
23
+ begin
24
+ authenticator.verify(padded_key, signature.encode('binary'), signing_input.encode('binary'))
25
+ rescue RbNaCl::BadAuthenticatorError
26
+ false
27
+ end
28
+ else
29
+ SecurityUtils.secure_compare(signature, sign(JWT::Signature::ToSign.new(algorithm, signing_input, public_key)))
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,43 @@
1
+ module JWT
2
+ module Algos
3
+ module Ps
4
+ # RSASSA-PSS signing algorithms
5
+
6
+ module_function
7
+
8
+ SUPPORTED = %w[PS256 PS384 PS512].freeze
9
+
10
+ def sign(to_sign)
11
+ require_openssl!
12
+
13
+ algorithm, msg, key = to_sign.values
14
+
15
+ key_class = key.class
16
+
17
+ raise EncodeError, "The given key is a #{key_class}. It has to be an OpenSSL::PKey::RSA instance." if key_class == String
18
+
19
+ translated_algorithm = algorithm.sub('PS', 'sha')
20
+
21
+ key.sign_pss(translated_algorithm, msg, salt_length: :digest, mgf1_hash: translated_algorithm)
22
+ end
23
+
24
+ def verify(to_verify)
25
+ require_openssl!
26
+
27
+ SecurityUtils.verify_ps(to_verify.algorithm, to_verify.public_key, to_verify.signing_input, to_verify.signature)
28
+ end
29
+
30
+ def require_openssl!
31
+ if Object.const_defined?('OpenSSL')
32
+ major, minor = OpenSSL::VERSION.split('.').first(2)
33
+
34
+ unless major.to_i >= 2 && minor.to_i >= 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,19 @@
1
+ module JWT
2
+ module Algos
3
+ module Rsa
4
+ module_function
5
+
6
+ SUPPORTED = %w[RS256 RS384 RS512].freeze
7
+
8
+ def sign(to_sign)
9
+ 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
11
+ key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
12
+ end
13
+
14
+ def verify(to_verify)
15
+ SecurityUtils.verify_rsa(to_verify.algorithm, to_verify.public_key, to_verify.signing_input, to_verify.signature)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ module JWT
2
+ module Algos
3
+ module Unsupported
4
+ module_function
5
+
6
+ SUPPORTED = Object.new.tap { |object| object.define_singleton_method(:include?) { |*| true } }
7
+ def verify(*)
8
+ raise JWT::VerificationError, 'Algorithm not supported'
9
+ end
10
+
11
+ def sign(*)
12
+ raise NotImplementedError, 'Unsupported signing method'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -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,33 @@
1
+ require_relative './error'
2
+
3
+ module JWT
4
+ class ClaimsValidator
5
+ INTEGER_CLAIMS = %i[
6
+ exp
7
+ iat
8
+ nbf
9
+ ].freeze
10
+
11
+ def initialize(payload)
12
+ @payload = payload.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
13
+ end
14
+
15
+ def validate!
16
+ validate_int_claims
17
+
18
+ true
19
+ end
20
+
21
+ private
22
+
23
+ def validate_int_claims
24
+ INTEGER_CLAIMS.each do |claim|
25
+ validate_is_int(claim) if @payload.key?(claim)
26
+ end
27
+ end
28
+
29
+ def validate_is_int(claim)
30
+ raise InvalidPayload, "#{claim} claim must be an Integer but it is a #{@payload[claim].class}" unless @payload[claim].is_a?(Integer)
31
+ end
32
+ end
33
+ end
@@ -1,57 +1,107 @@
1
1
  # frozen_string_literal: true
2
- require 'jwt/json'
3
- require 'jwt/verify'
4
2
 
3
+ require 'json'
4
+
5
+ require 'jwt/signature'
6
+ require 'jwt/verify'
5
7
  # JWT::Decode module
6
8
  module JWT
7
- extend JWT::Json
8
-
9
9
  # Decoding logic for JWT
10
10
  class Decode
11
- attr_reader :header, :payload, :signature
12
-
13
11
  def initialize(jwt, key, verify, options, &keyfinder)
12
+ raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
14
13
  @jwt = jwt
15
14
  @key = key
16
- @verify = verify
17
15
  @options = options
16
+ @segments = jwt.split('.')
17
+ @verify = verify
18
+ @signature = ''
18
19
  @keyfinder = keyfinder
19
20
  end
20
21
 
21
22
  def decode_segments
22
- header_segment, payload_segment, crypto_segment = raw_segments(@jwt, @verify)
23
- @header, @payload = decode_header_and_payload(header_segment, payload_segment)
24
- @signature = Decode.base64url_decode(crypto_segment.to_s) if @verify
25
- signing_input = [header_segment, payload_segment].join('.')
26
- [@header, @payload, @signature, signing_input]
23
+ validate_segment_count!
24
+ if @verify
25
+ decode_crypto
26
+ verify_signature
27
+ verify_claims
28
+ end
29
+ raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
30
+ [payload, header]
27
31
  end
28
32
 
29
- def raw_segments(jwt, verify)
30
- segments = jwt.split('.')
31
- required_num_segments = verify ? [3] : [2, 3]
32
- raise(JWT::DecodeError, 'Not enough or too many segments') unless required_num_segments.include? segments.length
33
- segments
33
+ private
34
+
35
+ def verify_signature
36
+ raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') if allowed_algorithms.empty?
37
+ raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless options_includes_algo_in_header?
38
+
39
+ @key = find_key(&@keyfinder) if @keyfinder
40
+ @key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks]).key_for(header['kid']) if @options[:jwks]
41
+
42
+ Signature.verify(header['alg'], @key, signing_input, @signature)
34
43
  end
35
- private :raw_segments
36
44
 
37
- def decode_header_and_payload(header_segment, payload_segment)
38
- header = JWT.decode_json(Decode.base64url_decode(header_segment))
39
- payload = JWT.decode_json(Decode.base64url_decode(payload_segment))
40
- [header, payload]
45
+ def options_includes_algo_in_header?
46
+ allowed_algorithms.include? header['alg']
41
47
  end
42
- private :decode_header_and_payload
43
48
 
44
- def self.base64url_decode(str)
45
- str += '=' * (4 - str.length.modulo(4))
46
- Base64.decode64(str.tr('-_', '+/'))
49
+ def allowed_algorithms
50
+ # Order is very important - first check for string keys, next for symbols
51
+ if @options.key?('algorithm')
52
+ [@options['algorithm']]
53
+ elsif @options.key?(:algorithm)
54
+ [@options[:algorithm]]
55
+ elsif @options.key?('algorithms')
56
+ @options['algorithms'] || []
57
+ elsif @options.key?(:algorithms)
58
+ @options[:algorithms] || []
59
+ else
60
+ []
61
+ end
47
62
  end
48
63
 
49
- def verify
50
- @options.each do |key, val|
51
- next unless key.to_s =~ /verify/
64
+ def find_key(&keyfinder)
65
+ key = (keyfinder.arity == 2 ? yield(header, payload) : yield(header))
66
+ raise JWT::DecodeError, 'No verification key available' unless key
67
+ key
68
+ end
52
69
 
53
- Verify.send(key, payload, @options) if val
54
- end
70
+ def verify_claims
71
+ Verify.verify_claims(payload, @options)
72
+ end
73
+
74
+ def validate_segment_count!
75
+ return if segment_length == 3
76
+ return if !@verify && segment_length == 2 # If no verifying required, the signature is not needed
77
+
78
+ raise(JWT::DecodeError, 'Not enough or too many segments')
79
+ end
80
+
81
+ def segment_length
82
+ @segments.count
83
+ end
84
+
85
+ def decode_crypto
86
+ @signature = JWT::Base64.url_decode(@segments[2])
87
+ end
88
+
89
+ def header
90
+ @header ||= parse_and_decode @segments[0]
91
+ end
92
+
93
+ def payload
94
+ @payload ||= parse_and_decode @segments[1]
95
+ end
96
+
97
+ def signing_input
98
+ @segments.first(2).join('.')
99
+ end
100
+
101
+ def parse_and_decode(segment)
102
+ JWT::JSON.parse(JWT::Base64.url_decode(segment))
103
+ rescue ::JSON::ParserError
104
+ raise JWT::DecodeError, 'Invalid segment encoding'
55
105
  end
56
106
  end
57
107
  end