jwt 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c63d3d103ec14f12ea2b51b95ae8f5407dd7ace9
4
- data.tar.gz: f1de3f1e8ddfb79aa690a1f6d44492c70d037942
3
+ metadata.gz: 0fca109273d0c036454af123d30bb3eb75f0de39
4
+ data.tar.gz: 8848296d35465d3411f71d882da73ef05663f6a6
5
5
  SHA512:
6
- metadata.gz: 3b19fcd5018d17e4277a49abc5787cee37ff3991fc8cbcc97dbb00f50c3878fc08062d97e18e07cdaf5f8f2f09cfbf5a8937e11859ae9b44d90a8d5a20caa021
7
- data.tar.gz: f9df1adefd0f0d4525567372c8283e72639b2ee67320a893ef03d470bbbd6afd09a65725d3eb3153f8c68e7d40f693c7da3a5ed138ab766a23aa6ebbfe5c62f6
6
+ metadata.gz: 213d4ea31197a90be8b8cd08ea92dee4659f47b884bc3571440697db979cf98b04e3d1cf487bc94a7a8a8f3f29ee34ebf48d7cc5bd9cfa9f2ca65a092bb2c3d3
7
+ data.tar.gz: 530335d90320cdc5501cc1f67984502f79a390641b904567971ad4858a285128cc4702dbf54d505324bcb1ea3ecdf5675057c942e9709ecf0f17b4099229c04d
data/.ebert.yml CHANGED
@@ -1,4 +1,4 @@
1
- styleguide: plataformatec/linters
1
+ styleguide: excpt/linters
2
2
  engines:
3
3
  reek:
4
4
  enabled: true
@@ -6,6 +6,7 @@ engines:
6
6
  enabled: true
7
7
  rubocop:
8
8
  enabled: true
9
+ channel: rubocop-0-49
9
10
  duplication:
10
11
  config:
11
12
  languages:
@@ -1,5 +1,36 @@
1
1
  # Change Log
2
2
 
3
+ ## [2.1.0](https://github.com/jwt/ruby-jwt/tree/2.1.0) (2017-10-06)
4
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.0.0...2.1.0)
5
+
6
+ **Implemented enhancements:**
7
+
8
+ - Ed25519 support planned? [\#217](https://github.com/jwt/ruby-jwt/issues/217)
9
+ - Verify JTI Proc [\#207](https://github.com/jwt/ruby-jwt/issues/207)
10
+ - Allow a list of algorithms for decode [\#241](https://github.com/jwt/ruby-jwt/pull/241) ([lautis](https://github.com/lautis))
11
+ - verify takes 2 params, second being payload closes: \#207 [\#238](https://github.com/jwt/ruby-jwt/pull/238) ([ab320012](https://github.com/ab320012))
12
+ - simplified logic for keyfinder [\#237](https://github.com/jwt/ruby-jwt/pull/237) ([ab320012](https://github.com/ab320012))
13
+ - Show backtrace if rbnacl-libsodium not loaded [\#231](https://github.com/jwt/ruby-jwt/pull/231) ([buzztaiki](https://github.com/buzztaiki))
14
+ - Support for ED25519 [\#229](https://github.com/jwt/ruby-jwt/pull/229) ([ab320012](https://github.com/ab320012))
15
+
16
+ **Fixed bugs:**
17
+
18
+ - JWT.encode failing on encode for string [\#235](https://github.com/jwt/ruby-jwt/issues/235)
19
+ - The README says it uses an algorithm by default [\#226](https://github.com/jwt/ruby-jwt/issues/226)
20
+ - Fix string payload issue [\#236](https://github.com/jwt/ruby-jwt/pull/236) ([excpt](https://github.com/excpt))
21
+
22
+ **Closed issues:**
23
+
24
+ - Change from 1.5.6 to 2.0.0 and appears a "Completed 401 Unauthorized" [\#240](https://github.com/jwt/ruby-jwt/issues/240)
25
+ - Why doesn't the decode function use a default algorithm? [\#227](https://github.com/jwt/ruby-jwt/issues/227)
26
+
27
+ **Merged pull requests:**
28
+
29
+ - Update README.md [\#242](https://github.com/jwt/ruby-jwt/pull/242) ([excpt](https://github.com/excpt))
30
+ - Update ebert configuration [\#232](https://github.com/jwt/ruby-jwt/pull/232) ([excpt](https://github.com/excpt))
31
+ - added algos/strategy classes + structs for inputs [\#230](https://github.com/jwt/ruby-jwt/pull/230) ([ab320012](https://github.com/ab320012))
32
+ - Add HS256 algorithm to decode default options [\#228](https://github.com/jwt/ruby-jwt/pull/228) ([madkin10](https://github.com/madkin10))
33
+
3
34
  ## [v2.0.0](https://github.com/jwt/ruby-jwt/tree/v2.0.0) (2017-09-03)
4
35
  [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.0.0.beta1...v2.0.0)
5
36
 
@@ -18,6 +49,7 @@
18
49
 
19
50
  **Merged pull requests:**
20
51
 
52
+ - Release 2.0.0 preparations :\) [\#225](https://github.com/jwt/ruby-jwt/pull/225) ([excpt](https://github.com/excpt))
21
53
  - Skip 'exp' claim validation for array payloads [\#224](https://github.com/jwt/ruby-jwt/pull/224) ([excpt](https://github.com/excpt))
22
54
  - Use a default leeway of 0 [\#223](https://github.com/jwt/ruby-jwt/pull/223) ([travisofthenorth](https://github.com/travisofthenorth))
23
55
  - Fix reported codesmells [\#221](https://github.com/jwt/ruby-jwt/pull/221) ([excpt](https://github.com/excpt))
data/README.md CHANGED
@@ -61,9 +61,9 @@ decoded_token = JWT.decode token, nil, false
61
61
  puts decoded_token
62
62
  ```
63
63
 
64
- **HMAC** (default: HS256)
64
+ **HMAC**
65
65
 
66
- * HS256 - HMAC using SHA-256 hash algorithm (default)
66
+ * HS256 - HMAC using SHA-256 hash algorithm
67
67
  * HS512256 - HMAC using SHA-512-256 hash algorithm (only available with RbNaCl; see note below)
68
68
  * HS384 - HMAC using SHA-384 hash algorithm
69
69
  * HS512 - HMAC using SHA-512 hash algorithm
@@ -144,6 +144,35 @@ decoded_token = JWT.decode token, ecdsa_public, true, { :algorithm => 'ES256' }
144
144
  puts decoded_token
145
145
  ```
146
146
 
147
+ **EDDSA**
148
+
149
+ In order to use this algorithm you need to add the `RbNaCl` gem to you `Gemfile`.
150
+
151
+ ```ruby
152
+ gem 'rbnacl'
153
+ ```
154
+
155
+ For more detailed installation instruction check the official [repository](https://github.com/cryptosphere/rbnacl) on GitHub.
156
+
157
+ * ED25519
158
+
159
+ ```ruby
160
+ private_key = RbNaCl::Signatures::Ed25519::SigningKey.new("abcdefghijklmnopqrstuvwxyzABCDEF")
161
+ public_key = private_key.verify_key
162
+ token = JWT.encode payload, private_key, 'ED25519'
163
+
164
+ # eyJhbGciOiJFRDI1NTE5In0.eyJ0ZXN0IjoiZGF0YSJ9.-Ki0vxVOlsPXovPsYRT_9OXrLSgQd4RDAgCLY_PLmcP4q32RYy-yUUmX82ycegdekR9wo26me1wOzjmSU5nTCQ
165
+ puts token
166
+
167
+ decoded_token = JWT.decode token, public_key, true, {:algorithm => 'ED25519' }
168
+ # Array
169
+ # [
170
+ # {"test"=>"data"}, # payload
171
+ # {"alg"=>"ED25519"} # header
172
+ # ]
173
+
174
+ ```
175
+
147
176
  **RSASSA-PSS**
148
177
 
149
178
  Not implemented.
data/lib/jwt.rb CHANGED
@@ -41,21 +41,23 @@ module JWT
41
41
  def decode_verify_signature(key, header, payload, signature, signing_input, options, &keyfinder)
42
42
  algo, key = signature_algorithm_and_key(header, payload, key, &keyfinder)
43
43
 
44
- raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') unless options[:algorithm]
45
- raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless algo == options[:algorithm]
44
+ raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') if allowed_algorithms(options).empty?
45
+ raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless allowed_algorithms(options).include?(algo)
46
46
 
47
47
  Signature.verify(algo, key, signing_input, signature)
48
48
  end
49
49
 
50
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
58
- end
51
+ key = (keyfinder.arity == 2 ? yield(header, payload) : yield(header)) if keyfinder
52
+ raise JWT::DecodeError, 'No verification key available' unless key
59
53
  [header['alg'], key]
60
54
  end
55
+
56
+ def allowed_algorithms(options)
57
+ if options.key?(:algorithm)
58
+ [options[:algorithm]]
59
+ else
60
+ options[:algorithms] || []
61
+ end
62
+ end
61
63
  end
@@ -0,0 +1,35 @@
1
+ module JWT
2
+ module Algos
3
+ module Ecdsa
4
+ module_function
5
+
6
+ SUPPORTED = %(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,33 @@
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
+ authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, key)
11
+ if authenticator && padded_key
12
+ authenticator.auth(padded_key, msg.encode('binary'))
13
+ else
14
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
15
+ end
16
+ end
17
+
18
+ def verify(to_verify)
19
+ algorithm, public_key, signing_input, signature = to_verify.values
20
+ authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, public_key)
21
+ if authenticator && padded_key
22
+ begin
23
+ authenticator.verify(padded_key, signature.encode('binary'), signing_input.encode('binary'))
24
+ rescue RbNaCl::BadAuthenticatorError
25
+ false
26
+ end
27
+ else
28
+ SecurityUtils.secure_compare(signature, sign(JWT::Signature::ToSign.new(algorithm, signing_input, public_key)))
29
+ end
30
+ end
31
+ end
32
+ end
33
+ 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
@@ -8,7 +8,8 @@ module JWT
8
8
  verify_jti: false,
9
9
  verify_aud: false,
10
10
  verify_sub: false,
11
- leeway: 0
11
+ leeway: 0,
12
+ algorithms: ['HS256']
12
13
  }.freeze
13
14
  end
14
15
  end
@@ -28,7 +28,7 @@ module JWT
28
28
  end
29
29
 
30
30
  def encoded_payload
31
- raise InvalidPayload, 'exp claim must be an integer' if @payload && !@payload.is_a?(Array) && @payload.key?('exp') && !@payload['exp'].is_a?(Integer)
31
+ raise InvalidPayload, 'exp claim must be an integer' if @payload && @payload.is_a?(Hash) && @payload.key?('exp') && !@payload['exp'].is_a?(Integer)
32
32
  Encode.base64url_encode(JSON.generate(@payload))
33
33
  end
34
34
 
@@ -3,7 +3,6 @@ module JWT
3
3
  #
4
4
  # @see: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/security_utils.rb
5
5
  module SecurityUtils
6
-
7
6
  module_function
8
7
 
9
8
  def secure_compare(left, right)
@@ -2,10 +2,15 @@
2
2
 
3
3
  require 'jwt/security_utils'
4
4
  require 'openssl'
5
+ require 'jwt/algos/hmac'
6
+ require 'jwt/algos/eddsa'
7
+ require 'jwt/algos/ecdsa'
8
+ require 'jwt/algos/rsa'
9
+ require 'jwt/algos/unsupported'
5
10
  begin
6
11
  require 'rbnacl'
7
- rescue LoadError => e
8
- abort(e.message) if defined?(RbNaCl)
12
+ rescue LoadError
13
+ raise if defined?(RbNaCl)
9
14
  end
10
15
 
11
16
  # JWT::Signature module
@@ -13,94 +18,33 @@ module JWT
13
18
  # Signature logic for JWT
14
19
  module Signature
15
20
  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
21
+ ALGOS = [
22
+ Algos::Hmac,
23
+ Algos::Ecdsa,
24
+ Algos::Rsa,
25
+ Algos::Eddsa,
26
+ Algos::Unsupported
27
+ ].freeze
28
+ ToSign = Struct.new(:algorithm, :msg, :key)
29
+ ToVerify = Struct.new(:algorithm, :public_key, :signing_input, :signature)
26
30
 
27
31
  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'
32
+ algo = ALGOS.find do |alg|
33
+ alg.const_get(:SUPPORTED).include? algorithm
36
34
  end
35
+ algo.sign ToSign.new(algorithm, msg, key)
37
36
  end
38
37
 
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'
38
+ def verify(algorithm, key, signing_input, signature)
39
+ algo = ALGOS.find do |alg|
40
+ alg.const_get(:SUPPORTED).include? algorithm
48
41
  end
49
-
42
+ verified = algo.verify(ToVerify.new(algorithm, key, signing_input, signature))
50
43
  raise(JWT::VerificationError, 'Signature verification raised') unless verified
51
44
  rescue OpenSSL::PKey::PKeyError
52
45
  raise JWT::VerificationError, 'Signature verification raised'
53
46
  ensure
54
47
  OpenSSL.errors.clear
55
48
  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
49
  end
106
50
  end
@@ -52,9 +52,9 @@ module JWT
52
52
  return unless (options_iss = @options[:iss])
53
53
 
54
54
  iss = @payload['iss']
55
-
55
+
56
56
  return if Array(options_iss).map(&:to_s).include?(iss.to_s)
57
-
57
+
58
58
  raise(JWT::InvalidIssuerError, "Invalid issuer. Expected #{options_iss}, received #{iss || '<none>'}")
59
59
  end
60
60
 
@@ -63,7 +63,8 @@ module JWT
63
63
  jti = @payload['jti']
64
64
 
65
65
  if options_verify_jti.respond_to?(:call)
66
- raise(JWT::InvalidJtiError, 'Invalid jti') unless options_verify_jti.call(jti)
66
+ verified = options_verify_jti.arity == 2 ? options_verify_jti.call(jti, @payload) : options_verify_jti.call(jti)
67
+ raise(JWT::InvalidJtiError, 'Invalid jti') unless verified
67
68
  elsif jti.to_s.strip.empty?
68
69
  raise(JWT::InvalidJtiError, 'Missing jti')
69
70
  end
@@ -12,7 +12,7 @@ module JWT
12
12
  # major version
13
13
  MAJOR = 2
14
14
  # minor version
15
- MINOR = 0
15
+ MINOR = 1
16
16
  # tiny version
17
17
  TINY = 0
18
18
  # alpha, beta, etc. tag
@@ -182,6 +182,19 @@ module JWT
182
182
  it 'true proc should not raise JWT::InvalidJtiError' do
183
183
  Verify.verify_jti(payload, options.merge(verify_jti: ->(_jti) { true }))
184
184
  end
185
+
186
+ it 'it should not throw arguement error with 2 args' do
187
+ expect do
188
+ Verify.verify_jti(payload, options.merge(verify_jti: ->(_jti, pl) {
189
+ true
190
+ }))
191
+ end.to_not raise_error
192
+ end
193
+ it 'should have payload as second param in proc' do
194
+ Verify.verify_jti(payload, options.merge(verify_jti: ->(_jti, pl) {
195
+ expect(pl).to eq(payload)
196
+ }))
197
+ end
185
198
  end
186
199
 
187
200
  context '.verify_not_before(payload, options)' do
@@ -19,6 +19,8 @@ describe JWT do
19
19
  'ES384_public' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec384-public.pem'))),
20
20
  'ES512_private' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec512-private.pem'))),
21
21
  'ES512_public' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec512-public.pem'))),
22
+ 'ED25519_private' => RbNaCl::Signatures::Ed25519::SigningKey.new('abcdefghijklmnopqrstuvwxyzABCDEF'),
23
+ 'ED25519_public' => RbNaCl::Signatures::Ed25519::SigningKey.new('abcdefghijklmnopqrstuvwxyzABCDEF').verify_key,
22
24
  'NONE' => 'eyJhbGciOiJub25lIn0.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.',
23
25
  'HS256' => 'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.kWOVtIOpWcG7JnyJG0qOkTDbOy636XrrQhMm_8JrRQ8',
24
26
  'HS512256' => 'eyJhbGciOiJIUzUxMjI1NiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.Ds_4ibvf7z4QOBoKntEjDfthy3WJ-3rKMspTEcHE2bA',
@@ -132,6 +134,41 @@ describe JWT do
132
134
  end
133
135
  end
134
136
 
137
+ %w[ED25519].each do |alg|
138
+ context "alg: #{alg}" do
139
+ before(:each) do
140
+ data[alg] = JWT.encode payload, data["#{alg}_private"], alg
141
+ end
142
+
143
+ let(:wrong_key) { OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec256-wrong-public.pem'))) }
144
+
145
+ it 'should generate a valid token' do
146
+ jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
147
+
148
+ expect(header['alg']).to eq alg
149
+ expect(jwt_payload).to eq payload
150
+ end
151
+
152
+ it 'should decode a valid token' do
153
+ jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
154
+
155
+ expect(header['alg']).to eq alg
156
+ expect(jwt_payload).to eq payload
157
+ end
158
+
159
+ it 'wrong key should raise JWT::DecodeError' do
160
+ expect do
161
+ JWT.decode data[alg], wrong_key
162
+ end.to raise_error JWT::DecodeError
163
+ end
164
+
165
+ it 'wrong key and verify = false should not raise JWT::DecodeError' do
166
+ expect do
167
+ JWT.decode data[alg], wrong_key, false
168
+ end.not_to raise_error
169
+ end
170
+ end
171
+ end
135
172
  %w[ES256 ES384 ES512].each do |alg|
136
173
  context "alg: #{alg}" do
137
174
  before(:each) do
@@ -194,24 +231,39 @@ describe JWT do
194
231
 
195
232
  context 'Verify' do
196
233
  context 'algorithm' do
197
- it 'should raise JWT::IncorrectAlgorithm on missmatch' do
198
- token = JWT.encode payload, data[:secret], 'HS512'
234
+ it 'should raise JWT::IncorrectAlgorithm on mismatch' do
235
+ token = JWT.encode payload, data[:secret], 'HS256'
199
236
 
200
237
  expect do
201
238
  JWT.decode token, data[:secret], true, algorithm: 'HS384'
202
239
  end.to raise_error JWT::IncorrectAlgorithm
203
240
 
204
241
  expect do
205
- JWT.decode token, data[:secret], true, algorithm: 'HS512'
242
+ JWT.decode token, data[:secret], true, algorithm: 'HS256'
206
243
  end.not_to raise_error
207
244
  end
208
245
 
209
- it 'should raise JWT::IncorrectAlgorithm if no algorithm is provided' do
210
- token = JWT.encode payload, data[:rsa_public].to_s, 'HS256'
246
+ it 'should raise JWT::IncorrectAlgorithm when algorithms array does not contain algorithm' do
247
+ token = JWT.encode payload, data[:secret], 'HS512'
211
248
 
212
249
  expect do
213
- JWT.decode token, data[:rsa_public], true
250
+ JWT.decode token, data[:secret], true, algorithms: ['HS384']
214
251
  end.to raise_error JWT::IncorrectAlgorithm
252
+
253
+ expect do
254
+ JWT.decode token, data[:secret], true, algorithms: ['HS512', 'HS384']
255
+ end.not_to raise_error
256
+ end
257
+
258
+ context 'no algorithm provided' do
259
+ it 'should use the default decode algorithm' do
260
+ token = JWT.encode payload, data[:rsa_public].to_s
261
+
262
+ jwt_payload, header = JWT.decode token, data[:rsa_public].to_s
263
+
264
+ expect(header['alg']).to eq 'HS256'
265
+ expect(jwt_payload).to eq payload
266
+ end
215
267
  end
216
268
  end
217
269
 
@@ -254,4 +306,10 @@ describe JWT do
254
306
  JWT.encode(['my', 'payload'], 'secret')
255
307
  end.not_to raise_error
256
308
  end
309
+
310
+ it 'should encode string payloads' do
311
+ expect do
312
+ JWT.encode 'Hello World', 'secret'
313
+ end.not_to raise_error
314
+ end
257
315
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jwt
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Rudat
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-03 00:00:00.000000000 Z
11
+ date: 2017-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -143,6 +143,11 @@ files:
143
143
  - README.md
144
144
  - Rakefile
145
145
  - lib/jwt.rb
146
+ - lib/jwt/algos/ecdsa.rb
147
+ - lib/jwt/algos/eddsa.rb
148
+ - lib/jwt/algos/hmac.rb
149
+ - lib/jwt/algos/rsa.rb
150
+ - lib/jwt/algos/unsupported.rb
146
151
  - lib/jwt/decode.rb
147
152
  - lib/jwt/default_options.rb
148
153
  - lib/jwt/encode.rb