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 +4 -4
- data/.ebert.yml +2 -1
- data/CHANGELOG.md +32 -0
- data/README.md +31 -2
- data/lib/jwt.rb +12 -10
- data/lib/jwt/algos/ecdsa.rb +35 -0
- data/lib/jwt/algos/eddsa.rb +23 -0
- data/lib/jwt/algos/hmac.rb +33 -0
- data/lib/jwt/algos/rsa.rb +19 -0
- data/lib/jwt/algos/unsupported.rb +16 -0
- data/lib/jwt/default_options.rb +2 -1
- data/lib/jwt/encode.rb +1 -1
- data/lib/jwt/security_utils.rb +0 -1
- data/lib/jwt/signature.rb +23 -79
- data/lib/jwt/verify.rb +4 -3
- data/lib/jwt/version.rb +1 -1
- data/spec/jwt/verify_spec.rb +13 -0
- data/spec/jwt_spec.rb +64 -6
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0fca109273d0c036454af123d30bb3eb75f0de39
|
4
|
+
data.tar.gz: 8848296d35465d3411f71d882da73ef05663f6a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 213d4ea31197a90be8b8cd08ea92dee4659f47b884bc3571440697db979cf98b04e3d1cf487bc94a7a8a8f3f29ee34ebf48d7cc5bd9cfa9f2ca65a092bb2c3d3
|
7
|
+
data.tar.gz: 530335d90320cdc5501cc1f67984502f79a390641b904567971ad4858a285128cc4702dbf54d505324bcb1ea3ecdf5675057c942e9709ecf0f17b4099229c04d
|
data/.ebert.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -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**
|
64
|
+
**HMAC**
|
65
65
|
|
66
|
-
* HS256 - HMAC using SHA-256 hash algorithm
|
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')
|
45
|
-
raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless algo
|
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
|
-
|
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
|
data/lib/jwt/default_options.rb
CHANGED
data/lib/jwt/encode.rb
CHANGED
@@ -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 &&
|
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
|
|
data/lib/jwt/security_utils.rb
CHANGED
data/lib/jwt/signature.rb
CHANGED
@@ -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
|
8
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
29
|
-
|
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(
|
40
|
-
|
41
|
-
|
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
|
data/lib/jwt/verify.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/jwt/version.rb
CHANGED
data/spec/jwt/verify_spec.rb
CHANGED
@@ -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
|
data/spec/jwt_spec.rb
CHANGED
@@ -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
|
198
|
-
token = JWT.encode payload, data[:secret], '
|
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: '
|
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
|
210
|
-
token = JWT.encode payload, data[:
|
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[:
|
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.
|
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-
|
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
|