jwt 1.5.6 → 2.0.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.
@@ -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 }