jwt 2.0.0 → 2.1.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.
- 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
|