jwt 1.5.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,52 @@
1
+ module JWT
2
+ # Collection of security methods
3
+ #
4
+ # @see: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/security_utils.rb
5
+ module SecurityUtils
6
+
7
+ module_function
8
+
9
+ def secure_compare(left, right)
10
+ left_bytesize = left.bytesize
11
+
12
+ return false unless left_bytesize == right.bytesize
13
+
14
+ unpacked_left = left.unpack "C#{left_bytesize}"
15
+ result = 0
16
+ right.each_byte { |byte| result |= byte ^ unpacked_left.shift }
17
+ result.zero?
18
+ end
19
+
20
+ def verify_rsa(algorithm, public_key, signing_input, signature)
21
+ public_key.verify(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
22
+ end
23
+
24
+ def asn1_to_raw(signature, public_key)
25
+ byte_size = (public_key.group.degree + 7) / 8
26
+ OpenSSL::ASN1.decode(signature).value.map { |value| value.value.to_s(2).rjust(byte_size, "\x00") }.join
27
+ end
28
+
29
+ def raw_to_asn1(signature, private_key)
30
+ byte_size = (private_key.group.degree + 7) / 8
31
+ sig_bytes = signature[0..(byte_size - 1)]
32
+ sig_char = signature[byte_size..-1] || ''
33
+ OpenSSL::ASN1::Sequence.new([sig_bytes, sig_char].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
34
+ end
35
+
36
+ def rbnacl_fixup(algorithm, key)
37
+ algorithm = algorithm.sub('HS', 'SHA').to_sym
38
+
39
+ return [] unless defined?(RbNaCl) && RbNaCl::HMAC.constants(false).include?(algorithm)
40
+
41
+ authenticator = RbNaCl::HMAC.const_get(algorithm)
42
+
43
+ # Fall back to OpenSSL for keys larger than 32 bytes.
44
+ return [] if key.bytesize > authenticator.key_bytes
45
+
46
+ [
47
+ authenticator,
48
+ key.bytes.fill(0, key.bytesize...authenticator.key_bytes).pack('C*')
49
+ ]
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jwt/security_utils'
4
+ require 'openssl'
5
+ begin
6
+ require 'rbnacl'
7
+ rescue LoadError => e
8
+ abort(e.message) if defined?(RbNaCl)
9
+ end
10
+
11
+ # JWT::Signature module
12
+ module JWT
13
+ # Signature logic for JWT
14
+ module Signature
15
+ extend self
16
+
17
+ HMAC_ALGORITHMS = %w[HS256 HS512256 HS384 HS512].freeze
18
+ RSA_ALGORITHMS = %w[RS256 RS384 RS512].freeze
19
+ ECDSA_ALGORITHMS = %w[ES256 ES384 ES512].freeze
20
+
21
+ NAMED_CURVES = {
22
+ 'prime256v1' => 'ES256',
23
+ 'secp384r1' => 'ES384',
24
+ 'secp521r1' => 'ES512'
25
+ }.freeze
26
+
27
+ def sign(algorithm, msg, key)
28
+ if HMAC_ALGORITHMS.include?(algorithm)
29
+ sign_hmac(algorithm, msg, key)
30
+ elsif RSA_ALGORITHMS.include?(algorithm)
31
+ sign_rsa(algorithm, msg, key)
32
+ elsif ECDSA_ALGORITHMS.include?(algorithm)
33
+ sign_ecdsa(algorithm, msg, key)
34
+ else
35
+ raise NotImplementedError, 'Unsupported signing method'
36
+ end
37
+ end
38
+
39
+ def verify(algo, key, signing_input, signature)
40
+ verified = if HMAC_ALGORITHMS.include?(algo)
41
+ verify_hmac(algo, key, signing_input, signature)
42
+ elsif RSA_ALGORITHMS.include?(algo)
43
+ SecurityUtils.verify_rsa(algo, key, signing_input, signature)
44
+ elsif ECDSA_ALGORITHMS.include?(algo)
45
+ verify_ecdsa(algo, key, signing_input, signature)
46
+ else
47
+ raise JWT::VerificationError, 'Algorithm not supported'
48
+ end
49
+
50
+ raise(JWT::VerificationError, 'Signature verification raised') unless verified
51
+ rescue OpenSSL::PKey::PKeyError
52
+ raise JWT::VerificationError, 'Signature verification raised'
53
+ ensure
54
+ OpenSSL.errors.clear
55
+ end
56
+
57
+ private
58
+
59
+ def sign_rsa(algorithm, msg, private_key)
60
+ raise EncodeError, "The given key is a #{private_key.class}. It has to be an OpenSSL::PKey::RSA instance." if private_key.class == String
61
+ private_key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
62
+ end
63
+
64
+ def sign_ecdsa(algorithm, msg, private_key)
65
+ key_algorithm = NAMED_CURVES[private_key.group.curve_name]
66
+ if algorithm != key_algorithm
67
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
68
+ end
69
+
70
+ digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
71
+ SecurityUtils.asn1_to_raw(private_key.dsa_sign_asn1(digest.digest(msg)), private_key)
72
+ end
73
+
74
+ def sign_hmac(algorithm, msg, key)
75
+ authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, key)
76
+ if authenticator && padded_key
77
+ authenticator.auth(padded_key, msg.encode('binary'))
78
+ else
79
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
80
+ end
81
+ end
82
+
83
+ def verify_ecdsa(algorithm, public_key, signing_input, signature)
84
+ key_algorithm = NAMED_CURVES[public_key.group.curve_name]
85
+ if algorithm != key_algorithm
86
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
87
+ end
88
+
89
+ digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
90
+ public_key.dsa_verify_asn1(digest.digest(signing_input), SecurityUtils.raw_to_asn1(signature, public_key))
91
+ end
92
+
93
+ def verify_hmac(algorithm, public_key, signing_input, signature)
94
+ authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, public_key)
95
+ if authenticator && padded_key
96
+ begin
97
+ authenticator.verify(padded_key, signature.encode('binary'), signing_input.encode('binary'))
98
+ rescue RbNaCl::BadAuthenticatorError
99
+ false
100
+ end
101
+ else
102
+ SecurityUtils.secure_compare(signature, sign_hmac(algorithm, signing_input, public_key))
103
+ end
104
+ end
105
+ end
106
+ end
data/lib/jwt/verify.rb CHANGED
@@ -1,106 +1,101 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'jwt/error'
3
4
 
4
5
  module JWT
5
6
  # JWT verify methods
6
7
  class Verify
8
+ DEFAULTS = {
9
+ leeway: 0
10
+ }.freeze
11
+
7
12
  class << self
8
- %w(verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub).each do |method_name|
13
+ %w[verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub].each do |method_name|
9
14
  define_method method_name do |payload, options|
10
15
  new(payload, options).send(method_name)
11
16
  end
12
17
  end
18
+
19
+ def verify_claims(payload, options)
20
+ options.each do |key, val|
21
+ next unless key.to_s =~ /verify/
22
+ Verify.send(key, payload, options) if val
23
+ end
24
+ end
13
25
  end
14
26
 
15
27
  def initialize(payload, options)
16
28
  @payload = payload
17
- @options = options
29
+ @options = DEFAULTS.merge(options)
18
30
  end
19
31
 
20
32
  def verify_aud
21
- return unless (options_aud = extract_option(:aud))
22
-
23
- if @payload['aud'].is_a?(Array)
24
- verify_aud_array(@payload['aud'], options_aud)
25
- else
26
- raise(
27
- JWT::InvalidAudError,
28
- "Invalid audience. Expected #{options_aud}, received #{@payload['aud'] || '<none>'}"
29
- ) unless @payload['aud'].to_s == options_aud.to_s
30
- end
31
- end
33
+ return unless (options_aud = @options[:aud])
32
34
 
33
- def verify_aud_array(audience, options_aud)
34
- if options_aud.is_a?(Array)
35
- options_aud.each do |aud|
36
- raise(JWT::InvalidAudError, 'Invalid audience') unless audience.include?(aud.to_s)
37
- end
38
- else
39
- raise(JWT::InvalidAudError, 'Invalid audience') unless audience.include?(options_aud.to_s)
40
- end
35
+ aud = @payload['aud']
36
+ raise(JWT::InvalidAudError, "Invalid audience. Expected #{options_aud}, received #{aud || '<none>'}") if ([*aud] & [*options_aud]).empty?
41
37
  end
42
38
 
43
39
  def verify_expiration
44
40
  return unless @payload.include?('exp')
45
-
46
- if @payload['exp'].to_i <= (Time.now.to_i - leeway)
47
- raise(JWT::ExpiredSignature, 'Signature has expired')
48
- end
41
+ raise(JWT::ExpiredSignature, 'Signature has expired') if @payload['exp'].to_i <= (Time.now.to_i - exp_leeway)
49
42
  end
50
43
 
51
44
  def verify_iat
52
45
  return unless @payload.include?('iat')
53
46
 
54
- if !@payload['iat'].is_a?(Numeric) || @payload['iat'].to_f > (Time.now.to_f + leeway)
55
- raise(JWT::InvalidIatError, 'Invalid iat')
56
- end
47
+ iat = @payload['iat']
48
+ raise(JWT::InvalidIatError, 'Invalid iat') if !iat.is_a?(Numeric) || iat.to_f > (Time.now.to_f + iat_leeway)
57
49
  end
58
50
 
59
51
  def verify_iss
60
- return unless (options_iss = extract_option(:iss))
52
+ return unless (options_iss = @options[:iss])
61
53
 
62
- if @payload['iss'].to_s != options_iss.to_s
63
- raise(
64
- JWT::InvalidIssuerError,
65
- "Invalid issuer. Expected #{options_iss}, received #{@payload['iss'] || '<none>'}"
66
- )
67
- end
54
+ iss = @payload['iss']
55
+
56
+ return if Array(options_iss).map(&:to_s).include?(iss.to_s)
57
+
58
+ raise(JWT::InvalidIssuerError, "Invalid issuer. Expected #{options_iss}, received #{iss || '<none>'}")
68
59
  end
69
60
 
70
61
  def verify_jti
71
- options_verify_jti = extract_option(:verify_jti)
62
+ options_verify_jti = @options[:verify_jti]
63
+ jti = @payload['jti']
64
+
72
65
  if options_verify_jti.respond_to?(:call)
73
- raise(JWT::InvalidJtiError, 'Invalid jti') unless options_verify_jti.call(@payload['jti'])
74
- else
75
- raise(JWT::InvalidJtiError, 'Missing jti') if @payload['jti'].to_s.strip.empty?
66
+ raise(JWT::InvalidJtiError, 'Invalid jti') unless options_verify_jti.call(jti)
67
+ elsif jti.to_s.strip.empty?
68
+ raise(JWT::InvalidJtiError, 'Missing jti')
76
69
  end
77
70
  end
78
71
 
79
72
  def verify_not_before
80
73
  return unless @payload.include?('nbf')
81
-
82
- if @payload['nbf'].to_i > (Time.now.to_i + leeway)
83
- raise(JWT::ImmatureSignature, 'Signature nbf has not been reached')
84
- end
74
+ raise(JWT::ImmatureSignature, 'Signature nbf has not been reached') if @payload['nbf'].to_i > (Time.now.to_i + nbf_leeway)
85
75
  end
86
76
 
87
77
  def verify_sub
88
- return unless (options_sub = extract_option(:sub))
89
-
90
- raise(
91
- JWT::InvalidSubError,
92
- "Invalid subject. Expected #{options_sub}, received #{@payload['sub'] || '<none>'}"
93
- ) unless @payload['sub'].to_s == options_sub.to_s
78
+ return unless (options_sub = @options[:sub])
79
+ sub = @payload['sub']
80
+ raise(JWT::InvalidSubError, "Invalid subject. Expected #{options_sub}, received #{sub || '<none>'}") unless sub.to_s == options_sub.to_s
94
81
  end
95
82
 
96
83
  private
97
84
 
98
- def extract_option(key)
99
- @options.values_at(key.to_sym, key.to_s).compact.first
85
+ def global_leeway
86
+ @options[:leeway]
87
+ end
88
+
89
+ def exp_leeway
90
+ @options[:exp_leeway] || global_leeway
91
+ end
92
+
93
+ def iat_leeway
94
+ @options[:iat_leeway] || global_leeway
100
95
  end
101
96
 
102
- def leeway
103
- extract_option :leeway
97
+ def nbf_leeway
98
+ @options[:nbf_leeway] || global_leeway
104
99
  end
105
100
  end
106
101
  end
data/lib/jwt/version.rb CHANGED
@@ -10,11 +10,11 @@ module JWT
10
10
  # Moments version builder module
11
11
  module VERSION
12
12
  # major version
13
- MAJOR = 1
13
+ MAJOR = 2
14
14
  # minor version
15
- MINOR = 5
15
+ MINOR = 0
16
16
  # tiny version
17
- TINY = 6
17
+ TINY = 0
18
18
  # alpha, beta, etc. tag
19
19
  PRE = nil
20
20
 
data/lib/jwt.rb CHANGED
@@ -1,192 +1,61 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'base64'
3
- require 'openssl'
4
4
  require 'jwt/decode'
5
+ require 'jwt/default_options'
6
+ require 'jwt/encode'
5
7
  require 'jwt/error'
6
- require 'jwt/json'
8
+ require 'jwt/signature'
9
+ require 'jwt/verify'
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
21
+ encoder = Encode.new payload, key, algorithm, header_fields
22
+ encoder.segments
117
23
  end
118
24
 
119
25
  def decode(jwt, key = nil, verify = true, custom_options = {}, &keyfinder)
120
26
  raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
121
27
 
122
28
  merged_options = DEFAULT_OPTIONS.merge(custom_options)
123
- decoder = Decode.new jwt, key, verify, merged_options, &keyfinder
29
+
30
+ decoder = Decode.new jwt, verify
124
31
  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
32
+ decode_verify_signature(key, header, payload, signature, signing_input, merged_options, &keyfinder) if verify
33
+
34
+ Verify.verify_claims(payload, merged_options) if verify
127
35
 
128
36
  raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
129
37
 
130
38
  [payload, header]
131
39
  end
132
40
 
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
41
+ def decode_verify_signature(key, header, payload, signature, signing_input, options, &keyfinder)
42
+ algo, key = signature_algorithm_and_key(header, payload, key, &keyfinder)
140
43
 
141
- def signature_algorithm_and_key(header, key, &keyfinder)
142
- key = yield(header) if keyfinder
143
- [header['alg'], key]
144
- end
44
+ raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') unless options[:algorithm]
45
+ raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless algo == options[:algorithm]
145
46
 
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
47
+ Signature.verify(algo, key, signing_input, signature)
152
48
  end
153
49
 
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'
50
+ def signature_algorithm_and_key(header, payload, key, &keyfinder)
51
+ if keyfinder
52
+ key = if keyfinder.arity == 2
53
+ yield(header, payload)
54
+ else
55
+ yield(header)
56
+ end
57
+ raise JWT::DecodeError, 'No verification key available' unless key
163
58
  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
187
- end
188
-
189
- def base64url_decode(str)
190
- Decode.base64url_decode(str)
59
+ [header['alg'], key]
191
60
  end
192
61
  end
data/ruby-jwt.gemspec CHANGED
@@ -6,7 +6,6 @@ Gem::Specification.new do |spec|
6
6
  spec.name = 'jwt'
7
7
  spec.version = JWT.gem_version
8
8
  spec.authors = [
9
- 'Jeff Lindsay',
10
9
  'Tim Rudat'
11
10
  ]
12
11
  spec.email = 'timrudat@gmail.com'
@@ -14,17 +13,19 @@ Gem::Specification.new do |spec|
14
13
  spec.description = 'A pure ruby implementation of the RFC 7519 OAuth JSON Web Token (JWT) standard.'
15
14
  spec.homepage = 'http://github.com/jwt/ruby-jwt'
16
15
  spec.license = 'MIT'
16
+ spec.required_ruby_version = '>= 2.1'
17
17
 
18
18
  spec.files = `git ls-files -z`.split("\x0")
19
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
- spec.require_paths = %w(lib)
21
+ spec.require_paths = %w[lib]
22
22
 
23
23
  spec.add_development_dependency 'bundler'
24
24
  spec.add_development_dependency 'rake'
25
- spec.add_development_dependency 'json', '< 2.0'
26
25
  spec.add_development_dependency 'rspec'
27
26
  spec.add_development_dependency 'simplecov'
28
27
  spec.add_development_dependency 'simplecov-json'
29
28
  spec.add_development_dependency 'codeclimate-test-reporter'
29
+ spec.add_development_dependency 'codacy-coverage'
30
+ spec.add_development_dependency 'rbnacl'
30
31
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative '../spec_helper'
3
4
  require 'jwt'
4
5
 
@@ -10,10 +11,10 @@ describe 'README.md code test' do
10
11
  token = JWT.encode payload, nil, 'none'
11
12
  decoded_token = JWT.decode token, nil, false
12
13
 
13
- expect(token).to eq 'eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.'
14
+ expect(token).to eq 'eyJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.'
14
15
  expect(decoded_token).to eq [
15
16
  { 'data' => 'test' },
16
- { 'typ' => 'JWT', 'alg' => 'none' }
17
+ { 'alg' => 'none' }
17
18
  ]
18
19
  end
19
20
 
@@ -21,10 +22,10 @@ describe 'README.md code test' do
21
22
  token = JWT.encode payload, 'my$ecretK3y', 'HS256'
22
23
  decoded_token = JWT.decode token, 'my$ecretK3y', false
23
24
 
24
- expect(token).to eq 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.ZxW8go9hz3ETCSfxFxpwSkYg_602gOPKearsf6DsxgY'
25
+ expect(token).to eq 'eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.pNIWIL34Jo13LViZAJACzK6Yf0qnvT_BuwOxiMCPE-Y'
25
26
  expect(decoded_token).to eq [
26
27
  { 'data' => 'test' },
27
- { 'typ' => 'JWT', 'alg' => 'HS256' }
28
+ { 'alg' => 'HS256' }
28
29
  ]
29
30
  end
30
31
 
@@ -37,7 +38,7 @@ describe 'README.md code test' do
37
38
 
38
39
  expect(decoded_token).to eq [
39
40
  { 'data' => 'test' },
40
- { 'typ' => 'JWT', 'alg' => 'RS256' }
41
+ { 'alg' => 'RS256' }
41
42
  ]
42
43
  end
43
44
 
@@ -52,7 +53,7 @@ describe 'README.md code test' do
52
53
 
53
54
  expect(decoded_token).to eq [
54
55
  { 'data' => 'test' },
55
- { 'typ' => 'JWT', 'alg' => 'ES256' }
56
+ { 'alg' => 'ES256' }
56
57
  ]
57
58
  end
58
59
  end
@@ -122,13 +123,13 @@ describe 'README.md code test' do
122
123
 
123
124
  context 'aud' do
124
125
  it 'array' do
125
- aud = %w(Young Old)
126
+ aud = %w[Young Old]
126
127
  aud_payload = { data: 'data', aud: aud }
127
128
 
128
129
  token = JWT.encode aud_payload, hmac_secret, 'HS256'
129
130
 
130
131
  expect do
131
- JWT.decode token, hmac_secret, true, aud: %w(Old Young), verify_aud: true, algorithm: 'HS256'
132
+ JWT.decode token, hmac_secret, true, aud: %w[Old Young], verify_aud: true, algorithm: 'HS256'
132
133
  end.not_to raise_error
133
134
  end
134
135
 
@@ -176,6 +177,17 @@ describe 'README.md code test' do
176
177
  end
177
178
  end
178
179
 
180
+ context 'custom header fields' do
181
+ it 'with custom field' do
182
+ payload = { data: 'test' }
183
+
184
+ token = JWT.encode payload, nil, 'none', typ: 'JWT'
185
+ _, header = JWT.decode token, nil, false
186
+
187
+ expect(header['typ']).to eq 'JWT'
188
+ end
189
+ end
190
+
179
191
  it 'sub' do
180
192
  sub = 'Subject'
181
193
  sub_payload = { data: 'data', sub: sub }