jwt 2.0.0.beta1 → 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.
- checksums.yaml +4 -4
- data/.ebert.yml +17 -0
- data/.reek.yml +40 -0
- data/.rubocop.yml +95 -2
- data/.travis.yml +1 -0
- data/CHANGELOG.md +36 -3
- data/Gemfile +0 -1
- data/README.md +10 -3
- data/lib/jwt.rb +6 -12
- data/lib/jwt/decode.rb +8 -4
- data/lib/jwt/encode.rb +13 -13
- data/lib/jwt/error.rb +1 -0
- data/lib/jwt/security_utils.rb +52 -0
- data/lib/jwt/signature.rb +20 -59
- data/lib/jwt/verify.rb +25 -8
- data/lib/jwt/version.rb +1 -1
- data/ruby-jwt.gemspec +2 -2
- data/spec/integration/readme_examples_spec.rb +3 -2
- data/spec/jwt/verify_spec.rb +45 -16
- data/spec/jwt_spec.rb +27 -3
- data/spec/spec_helper.rb +1 -1
- metadata +9 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c63d3d103ec14f12ea2b51b95ae8f5407dd7ace9
|
|
4
|
+
data.tar.gz: f1de3f1e8ddfb79aa690a1f6d44492c70d037942
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b19fcd5018d17e4277a49abc5787cee37ff3991fc8cbcc97dbb00f50c3878fc08062d97e18e07cdaf5f8f2f09cfbf5a8937e11859ae9b44d90a8d5a20caa021
|
|
7
|
+
data.tar.gz: f9df1adefd0f0d4525567372c8283e72639b2ee67320a893ef03d470bbbd6afd09a65725d3eb3153f8c68e7d40f693c7da3a5ed138ab766a23aa6ebbfe5c62f6
|
data/.ebert.yml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
styleguide: plataformatec/linters
|
|
2
|
+
engines:
|
|
3
|
+
reek:
|
|
4
|
+
enabled: true
|
|
5
|
+
fixme:
|
|
6
|
+
enabled: true
|
|
7
|
+
rubocop:
|
|
8
|
+
enabled: true
|
|
9
|
+
duplication:
|
|
10
|
+
config:
|
|
11
|
+
languages:
|
|
12
|
+
- ruby
|
|
13
|
+
enabled: true
|
|
14
|
+
remark-lint:
|
|
15
|
+
enabled: true
|
|
16
|
+
exclude_paths:
|
|
17
|
+
- spec
|
data/.reek.yml
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
TooManyStatements:
|
|
3
|
+
max_statements: 10
|
|
4
|
+
UncommunicativeMethodName:
|
|
5
|
+
reject:
|
|
6
|
+
- !ruby/regexp /^[a-z]$/
|
|
7
|
+
- !ruby/regexp /[0-9]$/
|
|
8
|
+
UncommunicativeParameterName:
|
|
9
|
+
reject:
|
|
10
|
+
- !ruby/regexp /^.$/
|
|
11
|
+
- !ruby/regexp /[0-9]$/
|
|
12
|
+
- !ruby/regexp /^_/
|
|
13
|
+
UncommunicativeVariableName:
|
|
14
|
+
reject:
|
|
15
|
+
- !ruby/regexp /^.$/
|
|
16
|
+
- !ruby/regexp /[0-9]$/
|
|
17
|
+
UtilityFunction:
|
|
18
|
+
enabled: false
|
|
19
|
+
LongParameterList:
|
|
20
|
+
enabled: false
|
|
21
|
+
DuplicateMethodCall:
|
|
22
|
+
max_calls: 2
|
|
23
|
+
IrresponsibleModule:
|
|
24
|
+
enabled: false
|
|
25
|
+
NestedIterators:
|
|
26
|
+
max_allowed_nesting: 2
|
|
27
|
+
PrimaDonnaMethod:
|
|
28
|
+
enabled: false
|
|
29
|
+
UnusedParameters:
|
|
30
|
+
enabled: false
|
|
31
|
+
FeatureEnvy:
|
|
32
|
+
enabled: false
|
|
33
|
+
ControlParameter:
|
|
34
|
+
enabled: false
|
|
35
|
+
UnusedPrivateMethod:
|
|
36
|
+
enabled: false
|
|
37
|
+
InstanceVariableAssumption:
|
|
38
|
+
exclude:
|
|
39
|
+
- !ruby/regexp /Controller$/
|
|
40
|
+
- !ruby/regexp /Mailer$/s
|
data/.rubocop.yml
CHANGED
|
@@ -1,5 +1,98 @@
|
|
|
1
1
|
AllCops:
|
|
2
|
-
|
|
3
|
-
-
|
|
2
|
+
Exclude:
|
|
3
|
+
- 'bin/**/*'
|
|
4
|
+
- 'db/**/*'
|
|
5
|
+
- 'config/**/*'
|
|
6
|
+
- 'script/**/*'
|
|
7
|
+
|
|
8
|
+
Rails:
|
|
9
|
+
Enabled: true
|
|
10
|
+
|
|
11
|
+
Style/AlignParameters:
|
|
12
|
+
EnforcedStyle: with_fixed_indentation
|
|
13
|
+
|
|
14
|
+
Style/CaseIndentation:
|
|
15
|
+
EnforcedStyle: end
|
|
16
|
+
|
|
17
|
+
Style/AsciiComments:
|
|
18
|
+
Enabled: false
|
|
19
|
+
|
|
20
|
+
Style/IndentHash:
|
|
21
|
+
Enabled: false
|
|
22
|
+
|
|
23
|
+
Style/CollectionMethods:
|
|
24
|
+
Enabled: true
|
|
25
|
+
PreferredMethods:
|
|
26
|
+
inject: 'inject'
|
|
27
|
+
|
|
28
|
+
Style/Documentation:
|
|
29
|
+
Enabled: false
|
|
30
|
+
|
|
31
|
+
Style/BlockDelimiters:
|
|
32
|
+
Exclude:
|
|
33
|
+
- spec/**/*_spec.rb
|
|
34
|
+
|
|
35
|
+
Style/BracesAroundHashParameters:
|
|
36
|
+
Exclude:
|
|
37
|
+
- spec/**/*_spec.rb
|
|
38
|
+
|
|
39
|
+
Style/GuardClause:
|
|
40
|
+
Enabled: false
|
|
41
|
+
|
|
42
|
+
Style/IfUnlessModifier:
|
|
43
|
+
Enabled: false
|
|
44
|
+
|
|
45
|
+
Style/SpaceInsideHashLiteralBraces:
|
|
46
|
+
Enabled: false
|
|
47
|
+
|
|
48
|
+
Style/Lambda:
|
|
49
|
+
Enabled: false
|
|
50
|
+
|
|
51
|
+
Style/RaiseArgs:
|
|
52
|
+
Enabled: false
|
|
53
|
+
|
|
54
|
+
Style/SignalException:
|
|
55
|
+
Enabled: false
|
|
56
|
+
|
|
57
|
+
Metrics/AbcSize:
|
|
58
|
+
Max: 20
|
|
59
|
+
|
|
60
|
+
Metrics/ClassLength:
|
|
61
|
+
Max: 100
|
|
62
|
+
|
|
63
|
+
Metrics/ModuleLength:
|
|
64
|
+
Max: 100
|
|
65
|
+
|
|
4
66
|
Metrics/LineLength:
|
|
5
67
|
Enabled: false
|
|
68
|
+
|
|
69
|
+
Metrics/MethodLength:
|
|
70
|
+
Max: 15
|
|
71
|
+
|
|
72
|
+
Style/SingleLineBlockParams:
|
|
73
|
+
Enabled: false
|
|
74
|
+
|
|
75
|
+
Lint/EndAlignment:
|
|
76
|
+
EnforcedStyleAlignWith: variable
|
|
77
|
+
|
|
78
|
+
Style/FormatString:
|
|
79
|
+
Enabled: false
|
|
80
|
+
|
|
81
|
+
Style/MultilineMethodCallIndentation:
|
|
82
|
+
EnforcedStyle: indented
|
|
83
|
+
|
|
84
|
+
Style/MultilineOperationIndentation:
|
|
85
|
+
EnforcedStyle: indented
|
|
86
|
+
|
|
87
|
+
Style/WordArray:
|
|
88
|
+
Enabled: false
|
|
89
|
+
|
|
90
|
+
Style/RedundantSelf:
|
|
91
|
+
Enabled: false
|
|
92
|
+
|
|
93
|
+
Style/AlignHash:
|
|
94
|
+
Enabled: true
|
|
95
|
+
EnforcedLastArgumentHashStyle: always_ignore
|
|
96
|
+
|
|
97
|
+
Style/TrivialAccessors:
|
|
98
|
+
AllowPredicates: true
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,39 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
## [v2.0.0](https://github.com/jwt/ruby-jwt/tree/v2.0.0) (2017-
|
|
4
|
-
[Full Changelog](https://github.com/jwt/ruby-jwt/compare/
|
|
3
|
+
## [v2.0.0](https://github.com/jwt/ruby-jwt/tree/v2.0.0) (2017-09-03)
|
|
4
|
+
[Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.0.0.beta1...v2.0.0)
|
|
5
|
+
|
|
6
|
+
**Fixed bugs:**
|
|
7
|
+
|
|
8
|
+
- Support versions outside 2.1 [\#209](https://github.com/jwt/ruby-jwt/issues/209)
|
|
9
|
+
- Verifying expiration without leeway throws exception [\#206](https://github.com/jwt/ruby-jwt/issues/206)
|
|
10
|
+
- Ruby interpreter warning [\#200](https://github.com/jwt/ruby-jwt/issues/200)
|
|
11
|
+
- TypeError: no implicit conversion of String into Integer [\#188](https://github.com/jwt/ruby-jwt/issues/188)
|
|
12
|
+
- Fix JWT.encode\(nil\) [\#203](https://github.com/jwt/ruby-jwt/pull/203) ([tmm1](https://github.com/tmm1))
|
|
13
|
+
|
|
14
|
+
**Closed issues:**
|
|
15
|
+
|
|
16
|
+
- Possibility to disable claim verifications [\#222](https://github.com/jwt/ruby-jwt/issues/222)
|
|
17
|
+
- Proper way to verify Firebase id tokens [\#216](https://github.com/jwt/ruby-jwt/issues/216)
|
|
18
|
+
|
|
19
|
+
**Merged pull requests:**
|
|
20
|
+
|
|
21
|
+
- Skip 'exp' claim validation for array payloads [\#224](https://github.com/jwt/ruby-jwt/pull/224) ([excpt](https://github.com/excpt))
|
|
22
|
+
- Use a default leeway of 0 [\#223](https://github.com/jwt/ruby-jwt/pull/223) ([travisofthenorth](https://github.com/travisofthenorth))
|
|
23
|
+
- Fix reported codesmells [\#221](https://github.com/jwt/ruby-jwt/pull/221) ([excpt](https://github.com/excpt))
|
|
24
|
+
- Add fancy gem version badge [\#220](https://github.com/jwt/ruby-jwt/pull/220) ([excpt](https://github.com/excpt))
|
|
25
|
+
- Add missing dist option to .travis.yml [\#219](https://github.com/jwt/ruby-jwt/pull/219) ([excpt](https://github.com/excpt))
|
|
26
|
+
- Fix ruby version requirements in gemspec file [\#218](https://github.com/jwt/ruby-jwt/pull/218) ([excpt](https://github.com/excpt))
|
|
27
|
+
- Fix a little typo in the readme [\#214](https://github.com/jwt/ruby-jwt/pull/214) ([RyanBrushett](https://github.com/RyanBrushett))
|
|
28
|
+
- Update README.md [\#212](https://github.com/jwt/ruby-jwt/pull/212) ([zuzannast](https://github.com/zuzannast))
|
|
29
|
+
- Fix typo in HS512256 algorithm description [\#211](https://github.com/jwt/ruby-jwt/pull/211) ([ojab](https://github.com/ojab))
|
|
30
|
+
- Allow configuration of multiple acceptable issuers [\#210](https://github.com/jwt/ruby-jwt/pull/210) ([ojab](https://github.com/ojab))
|
|
31
|
+
- Enforce `exp` to be an `Integer` [\#205](https://github.com/jwt/ruby-jwt/pull/205) ([lucasmazza](https://github.com/lucasmazza))
|
|
32
|
+
- ruby 1.9.3 support message upd [\#204](https://github.com/jwt/ruby-jwt/pull/204) ([maokomioko](https://github.com/maokomioko))
|
|
33
|
+
- Guard against partially loaded RbNaCl when failing to load libsodium [\#202](https://github.com/jwt/ruby-jwt/pull/202) ([Dorian](https://github.com/Dorian))
|
|
34
|
+
|
|
35
|
+
## [v2.0.0.beta1](https://github.com/jwt/ruby-jwt/tree/v2.0.0.beta1) (2017-02-27)
|
|
36
|
+
[Full Changelog](https://github.com/jwt/ruby-jwt/compare/v1.5.6...v2.0.0.beta1)
|
|
5
37
|
|
|
6
38
|
**Implemented enhancements:**
|
|
7
39
|
|
|
@@ -36,6 +68,8 @@
|
|
|
36
68
|
|
|
37
69
|
**Merged pull requests:**
|
|
38
70
|
|
|
71
|
+
- Version bump 2.0.0.beta1 [\#199](https://github.com/jwt/ruby-jwt/pull/199) ([excpt](https://github.com/excpt))
|
|
72
|
+
- Update CHANGELOG.md and minor fixes [\#198](https://github.com/jwt/ruby-jwt/pull/198) ([excpt](https://github.com/excpt))
|
|
39
73
|
- Add Codacy coverage reporter [\#194](https://github.com/jwt/ruby-jwt/pull/194) ([excpt](https://github.com/excpt))
|
|
40
74
|
- Add minimum required ruby version to gemspec [\#193](https://github.com/jwt/ruby-jwt/pull/193) ([excpt](https://github.com/excpt))
|
|
41
75
|
- Code smell fixes [\#192](https://github.com/jwt/ruby-jwt/pull/192) ([excpt](https://github.com/excpt))
|
|
@@ -301,7 +335,6 @@
|
|
|
301
335
|
|
|
302
336
|
**Closed issues:**
|
|
303
337
|
|
|
304
|
-
- yanking of version 0.1.12 causes issues [\#39](https://github.com/jwt/ruby-jwt/issues/39)
|
|
305
338
|
- Semantic versioning [\#37](https://github.com/jwt/ruby-jwt/issues/37)
|
|
306
339
|
- Update gem to get latest changes [\#36](https://github.com/jwt/ruby-jwt/issues/36)
|
|
307
340
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# JWT
|
|
2
2
|
|
|
3
|
+
[](https://badge.fury.io/rb/jwt)
|
|
3
4
|
[](https://travis-ci.org/jwt/ruby-jwt)
|
|
4
5
|
[](https://codeclimate.com/github/jwt/ruby-jwt)
|
|
5
6
|
[](https://codeclimate.com/github/jwt/ruby-jwt/coverage)
|
|
@@ -11,7 +12,7 @@ If you have further questions related to development or usage, join us: [ruby-jw
|
|
|
11
12
|
|
|
12
13
|
## Announcements
|
|
13
14
|
|
|
14
|
-
* Ruby 1.9.3 support
|
|
15
|
+
* Ruby 1.9.3 support was dropped at December 31st, 2016.
|
|
15
16
|
* Version 1.5.3 yanked. See: [#132](https://github.com/jwt/ruby-jwt/issues/132) and [#133](https://github.com/jwt/ruby-jwt/issues/133)
|
|
16
17
|
|
|
17
18
|
## Installing
|
|
@@ -63,7 +64,7 @@ puts decoded_token
|
|
|
63
64
|
**HMAC** (default: HS256)
|
|
64
65
|
|
|
65
66
|
* HS256 - HMAC using SHA-256 hash algorithm (default)
|
|
66
|
-
* HS512256 - HMAC using SHA-512
|
|
67
|
+
* HS512256 - HMAC using SHA-512-256 hash algorithm (only available with RbNaCl; see note below)
|
|
67
68
|
* HS384 - HMAC using SHA-384 hash algorithm
|
|
68
69
|
* HS512 - HMAC using SHA-512 hash algorithm
|
|
69
70
|
|
|
@@ -85,7 +86,11 @@ decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' }
|
|
|
85
86
|
puts decoded_token
|
|
86
87
|
```
|
|
87
88
|
|
|
88
|
-
Note: If [RbNaCl](https://github.com/cryptosphere/rbnacl) is loadable, ruby-jwt will use it for HMAC-SHA256, HMAC-SHA512
|
|
89
|
+
Note: If [RbNaCl](https://github.com/cryptosphere/rbnacl) is loadable, ruby-jwt will use it for HMAC-SHA256, HMAC-SHA512-256, and HMAC-SHA512. RbNaCl enforces a maximum key size of 32 bytes for these algorithms.
|
|
90
|
+
|
|
91
|
+
[RbNaCl](https://github.com/cryptosphere/rbnacl) requires
|
|
92
|
+
[libsodium](https://github.com/jedisct1/libsodium), it can be installed
|
|
93
|
+
on MacOS with `brew install libsodium`.
|
|
89
94
|
|
|
90
95
|
**RSA**
|
|
91
96
|
|
|
@@ -273,6 +278,8 @@ From [Oauth JSON Web Token 4.1.1. "iss" (Issuer) Claim](https://tools.ietf.org/h
|
|
|
273
278
|
|
|
274
279
|
> The `iss` (issuer) claim identifies the principal that issued the JWT. The processing of this claim is generally application specific. The `iss` value is a case-sensitive string containing a ***StringOrURI*** value. Use of this claim is OPTIONAL.
|
|
275
280
|
|
|
281
|
+
You can pass multiple allowed issuers as an Array, verification will pass if one of them matches the `iss` value in the payload.
|
|
282
|
+
|
|
276
283
|
```ruby
|
|
277
284
|
iss = 'My Awesome Company Inc. or https://my.awesome.website/'
|
|
278
285
|
iss_payload = { :data => 'data', :iss => iss }
|
data/lib/jwt.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'base64'
|
|
3
4
|
require 'jwt/decode'
|
|
4
5
|
require 'jwt/default_options'
|
|
@@ -16,13 +17,6 @@ module JWT
|
|
|
16
17
|
|
|
17
18
|
module_function
|
|
18
19
|
|
|
19
|
-
def decoded_segments(jwt, verify = true)
|
|
20
|
-
raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
|
|
21
|
-
|
|
22
|
-
decoder = Decode.new jwt, verify
|
|
23
|
-
decoder.decode_segments
|
|
24
|
-
end
|
|
25
|
-
|
|
26
20
|
def encode(payload, key, algorithm = 'HS256', header_fields = {})
|
|
27
21
|
encoder = Encode.new payload, key, algorithm, header_fields
|
|
28
22
|
encoder.segments
|
|
@@ -37,7 +31,7 @@ module JWT
|
|
|
37
31
|
header, payload, signature, signing_input = decoder.decode_segments
|
|
38
32
|
decode_verify_signature(key, header, payload, signature, signing_input, merged_options, &keyfinder) if verify
|
|
39
33
|
|
|
40
|
-
Verify.verify_claims(payload, merged_options)
|
|
34
|
+
Verify.verify_claims(payload, merged_options) if verify
|
|
41
35
|
|
|
42
36
|
raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
|
|
43
37
|
|
|
@@ -56,10 +50,10 @@ module JWT
|
|
|
56
50
|
def signature_algorithm_and_key(header, payload, key, &keyfinder)
|
|
57
51
|
if keyfinder
|
|
58
52
|
key = if keyfinder.arity == 2
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
yield(header, payload)
|
|
54
|
+
else
|
|
55
|
+
yield(header)
|
|
56
|
+
end
|
|
63
57
|
raise JWT::DecodeError, 'No verification key available' unless key
|
|
64
58
|
end
|
|
65
59
|
[header['alg'], key]
|
data/lib/jwt/decode.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'json'
|
|
3
4
|
|
|
4
5
|
# JWT::Decode module
|
|
@@ -15,10 +16,13 @@ module JWT
|
|
|
15
16
|
def initialize(jwt, verify)
|
|
16
17
|
@jwt = jwt
|
|
17
18
|
@verify = verify
|
|
19
|
+
@header = ''
|
|
20
|
+
@payload = ''
|
|
21
|
+
@signature = ''
|
|
18
22
|
end
|
|
19
23
|
|
|
20
24
|
def decode_segments
|
|
21
|
-
header_segment, payload_segment, crypto_segment = raw_segments
|
|
25
|
+
header_segment, payload_segment, crypto_segment = raw_segments
|
|
22
26
|
@header, @payload = decode_header_and_payload(header_segment, payload_segment)
|
|
23
27
|
@signature = Decode.base64url_decode(crypto_segment.to_s) if @verify
|
|
24
28
|
signing_input = [header_segment, payload_segment].join('.')
|
|
@@ -27,9 +31,9 @@ module JWT
|
|
|
27
31
|
|
|
28
32
|
private
|
|
29
33
|
|
|
30
|
-
def raw_segments
|
|
31
|
-
segments = jwt.split('.')
|
|
32
|
-
required_num_segments = verify ? [3] : [2, 3]
|
|
34
|
+
def raw_segments
|
|
35
|
+
segments = @jwt.split('.')
|
|
36
|
+
required_num_segments = @verify ? [3] : [2, 3]
|
|
33
37
|
raise(JWT::DecodeError, 'Not enough or too many segments') unless required_num_segments.include? segments.length
|
|
34
38
|
segments
|
|
35
39
|
end
|
data/lib/jwt/encode.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'json'
|
|
3
4
|
|
|
4
5
|
# JWT::Encode module
|
|
@@ -21,31 +22,30 @@ module JWT
|
|
|
21
22
|
|
|
22
23
|
private
|
|
23
24
|
|
|
24
|
-
def encoded_header
|
|
25
|
-
header = { 'alg' => algorithm }.merge(header_fields)
|
|
25
|
+
def encoded_header
|
|
26
|
+
header = { 'alg' => @algorithm }.merge(@header_fields)
|
|
26
27
|
Encode.base64url_encode(JSON.generate(header))
|
|
27
28
|
end
|
|
28
29
|
|
|
29
|
-
def encoded_payload
|
|
30
|
-
raise InvalidPayload, 'exp claim must be an integer' if payload
|
|
31
|
-
Encode.base64url_encode(JSON.generate(payload))
|
|
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)
|
|
32
|
+
Encode.base64url_encode(JSON.generate(@payload))
|
|
32
33
|
end
|
|
33
34
|
|
|
34
|
-
def encoded_signature(signing_input
|
|
35
|
-
if algorithm == 'none'
|
|
35
|
+
def encoded_signature(signing_input)
|
|
36
|
+
if @algorithm == 'none'
|
|
36
37
|
''
|
|
37
38
|
else
|
|
38
|
-
signature = JWT::Signature.sign(algorithm, signing_input, key)
|
|
39
|
+
signature = JWT::Signature.sign(@algorithm, signing_input, @key)
|
|
39
40
|
Encode.base64url_encode(signature)
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
def encode_segments
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
segments.join('.')
|
|
45
|
+
header = encoded_header
|
|
46
|
+
payload = encoded_payload
|
|
47
|
+
signature = encoded_signature([header, payload].join('.'))
|
|
48
|
+
[header, payload, signature].join('.')
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
end
|
data/lib/jwt/error.rb
CHANGED
|
@@ -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
|
data/lib/jwt/signature.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'jwt/security_utils'
|
|
2
4
|
require 'openssl'
|
|
3
5
|
begin
|
|
4
6
|
require 'rbnacl'
|
|
5
|
-
rescue LoadError
|
|
7
|
+
rescue LoadError => e
|
|
8
|
+
abort(e.message) if defined?(RbNaCl)
|
|
6
9
|
end
|
|
7
10
|
|
|
8
11
|
# JWT::Signature module
|
|
@@ -11,9 +14,9 @@ module JWT
|
|
|
11
14
|
module Signature
|
|
12
15
|
extend self
|
|
13
16
|
|
|
14
|
-
HMAC_ALGORITHMS = %w
|
|
15
|
-
RSA_ALGORITHMS = %w
|
|
16
|
-
ECDSA_ALGORITHMS = %w
|
|
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
|
|
17
20
|
|
|
18
21
|
NAMED_CURVES = {
|
|
19
22
|
'prime256v1' => 'ES256',
|
|
@@ -35,14 +38,14 @@ module JWT
|
|
|
35
38
|
|
|
36
39
|
def verify(algo, key, signing_input, signature)
|
|
37
40
|
verified = if HMAC_ALGORITHMS.include?(algo)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
46
49
|
|
|
47
50
|
raise(JWT::VerificationError, 'Signature verification raised') unless verified
|
|
48
51
|
rescue OpenSSL::PKey::PKeyError
|
|
@@ -65,11 +68,11 @@ module JWT
|
|
|
65
68
|
end
|
|
66
69
|
|
|
67
70
|
digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
|
|
68
|
-
asn1_to_raw(private_key.dsa_sign_asn1(digest.digest(msg)), private_key)
|
|
71
|
+
SecurityUtils.asn1_to_raw(private_key.dsa_sign_asn1(digest.digest(msg)), private_key)
|
|
69
72
|
end
|
|
70
73
|
|
|
71
74
|
def sign_hmac(algorithm, msg, key)
|
|
72
|
-
authenticator, padded_key = rbnacl_fixup(algorithm, key)
|
|
75
|
+
authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, key)
|
|
73
76
|
if authenticator && padded_key
|
|
74
77
|
authenticator.auth(padded_key, msg.encode('binary'))
|
|
75
78
|
else
|
|
@@ -77,10 +80,6 @@ module JWT
|
|
|
77
80
|
end
|
|
78
81
|
end
|
|
79
82
|
|
|
80
|
-
def verify_rsa(algorithm, public_key, signing_input, signature)
|
|
81
|
-
public_key.verify(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
83
|
def verify_ecdsa(algorithm, public_key, signing_input, signature)
|
|
85
84
|
key_algorithm = NAMED_CURVES[public_key.group.curve_name]
|
|
86
85
|
if algorithm != key_algorithm
|
|
@@ -88,11 +87,11 @@ module JWT
|
|
|
88
87
|
end
|
|
89
88
|
|
|
90
89
|
digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
|
|
91
|
-
public_key.dsa_verify_asn1(digest.digest(signing_input), raw_to_asn1(signature, public_key))
|
|
90
|
+
public_key.dsa_verify_asn1(digest.digest(signing_input), SecurityUtils.raw_to_asn1(signature, public_key))
|
|
92
91
|
end
|
|
93
92
|
|
|
94
93
|
def verify_hmac(algorithm, public_key, signing_input, signature)
|
|
95
|
-
authenticator, padded_key = rbnacl_fixup(algorithm, public_key)
|
|
94
|
+
authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, public_key)
|
|
96
95
|
if authenticator && padded_key
|
|
97
96
|
begin
|
|
98
97
|
authenticator.verify(padded_key, signature.encode('binary'), signing_input.encode('binary'))
|
|
@@ -100,46 +99,8 @@ module JWT
|
|
|
100
99
|
false
|
|
101
100
|
end
|
|
102
101
|
else
|
|
103
|
-
secure_compare(signature, sign_hmac(algorithm, signing_input, public_key))
|
|
102
|
+
SecurityUtils.secure_compare(signature, sign_hmac(algorithm, signing_input, public_key))
|
|
104
103
|
end
|
|
105
104
|
end
|
|
106
|
-
|
|
107
|
-
def asn1_to_raw(signature, public_key)
|
|
108
|
-
byte_size = (public_key.group.degree + 7) / 8
|
|
109
|
-
OpenSSL::ASN1.decode(signature).value.map { |value| value.value.to_s(2).rjust(byte_size, "\x00") }.join
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def raw_to_asn1(signature, private_key)
|
|
113
|
-
byte_size = (private_key.group.degree + 7) / 8
|
|
114
|
-
r = signature[0..(byte_size - 1)]
|
|
115
|
-
s = signature[byte_size..-1] || ''
|
|
116
|
-
OpenSSL::ASN1::Sequence.new([r, s].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def rbnacl_fixup(algorithm, key)
|
|
120
|
-
algorithm = algorithm.sub('HS', 'SHA').to_sym
|
|
121
|
-
|
|
122
|
-
return [] unless defined?(RbNaCl) && RbNaCl::HMAC.constants(false).include?(algorithm)
|
|
123
|
-
|
|
124
|
-
authenticator = RbNaCl::HMAC.const_get(algorithm)
|
|
125
|
-
|
|
126
|
-
# Fall back to OpenSSL for keys larger than 32 bytes.
|
|
127
|
-
return [] if key.bytesize > authenticator.key_bytes
|
|
128
|
-
|
|
129
|
-
[
|
|
130
|
-
authenticator,
|
|
131
|
-
key.bytes.fill(0, key.bytesize...authenticator.key_bytes).pack('C*')
|
|
132
|
-
]
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# From devise
|
|
136
|
-
# constant-time comparison algorithm to prevent timing attacks
|
|
137
|
-
def secure_compare(a, b)
|
|
138
|
-
return false if a.nil? || b.nil? || a.empty? || b.empty? || a.bytesize != b.bytesize
|
|
139
|
-
l = a.unpack "C#{a.bytesize}"
|
|
140
|
-
res = 0
|
|
141
|
-
b.each_byte { |byte| res |= byte ^ l.shift }
|
|
142
|
-
res.zero?
|
|
143
|
-
end
|
|
144
105
|
end
|
|
145
106
|
end
|
data/lib/jwt/verify.rb
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
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
|
|
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
|
|
@@ -21,12 +26,14 @@ module JWT
|
|
|
21
26
|
|
|
22
27
|
def initialize(payload, options)
|
|
23
28
|
@payload = payload
|
|
24
|
-
@options = options
|
|
29
|
+
@options = DEFAULTS.merge(options)
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
def verify_aud
|
|
28
33
|
return unless (options_aud = @options[:aud])
|
|
29
|
-
|
|
34
|
+
|
|
35
|
+
aud = @payload['aud']
|
|
36
|
+
raise(JWT::InvalidAudError, "Invalid audience. Expected #{options_aud}, received #{aud || '<none>'}") if ([*aud] & [*options_aud]).empty?
|
|
30
37
|
end
|
|
31
38
|
|
|
32
39
|
def verify_expiration
|
|
@@ -36,19 +43,28 @@ module JWT
|
|
|
36
43
|
|
|
37
44
|
def verify_iat
|
|
38
45
|
return unless @payload.include?('iat')
|
|
39
|
-
|
|
46
|
+
|
|
47
|
+
iat = @payload['iat']
|
|
48
|
+
raise(JWT::InvalidIatError, 'Invalid iat') if !iat.is_a?(Numeric) || iat.to_f > (Time.now.to_f + iat_leeway)
|
|
40
49
|
end
|
|
41
50
|
|
|
42
51
|
def verify_iss
|
|
43
52
|
return unless (options_iss = @options[:iss])
|
|
44
|
-
|
|
53
|
+
|
|
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>'}")
|
|
45
59
|
end
|
|
46
60
|
|
|
47
61
|
def verify_jti
|
|
48
62
|
options_verify_jti = @options[:verify_jti]
|
|
63
|
+
jti = @payload['jti']
|
|
64
|
+
|
|
49
65
|
if options_verify_jti.respond_to?(:call)
|
|
50
|
-
raise(JWT::InvalidJtiError, 'Invalid jti') unless options_verify_jti.call(
|
|
51
|
-
elsif
|
|
66
|
+
raise(JWT::InvalidJtiError, 'Invalid jti') unless options_verify_jti.call(jti)
|
|
67
|
+
elsif jti.to_s.strip.empty?
|
|
52
68
|
raise(JWT::InvalidJtiError, 'Missing jti')
|
|
53
69
|
end
|
|
54
70
|
end
|
|
@@ -60,7 +76,8 @@ module JWT
|
|
|
60
76
|
|
|
61
77
|
def verify_sub
|
|
62
78
|
return unless (options_sub = @options[:sub])
|
|
63
|
-
|
|
79
|
+
sub = @payload['sub']
|
|
80
|
+
raise(JWT::InvalidSubError, "Invalid subject. Expected #{options_sub}, received #{sub || '<none>'}") unless sub.to_s == options_sub.to_s
|
|
64
81
|
end
|
|
65
82
|
|
|
66
83
|
private
|
data/lib/jwt/version.rb
CHANGED
data/ruby-jwt.gemspec
CHANGED
|
@@ -13,12 +13,12 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
spec.description = 'A pure ruby implementation of the RFC 7519 OAuth JSON Web Token (JWT) standard.'
|
|
14
14
|
spec.homepage = 'http://github.com/jwt/ruby-jwt'
|
|
15
15
|
spec.license = 'MIT'
|
|
16
|
-
spec.required_ruby_version = '
|
|
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
|
|
21
|
+
spec.require_paths = %w[lib]
|
|
22
22
|
|
|
23
23
|
spec.add_development_dependency 'bundler'
|
|
24
24
|
spec.add_development_dependency 'rake'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require_relative '../spec_helper'
|
|
3
4
|
require 'jwt'
|
|
4
5
|
|
|
@@ -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
|
|
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
|
|
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
|
|
data/spec/jwt/verify_spec.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'spec_helper'
|
|
3
4
|
require 'jwt/verify'
|
|
4
5
|
|
|
@@ -9,7 +10,7 @@ module JWT
|
|
|
9
10
|
|
|
10
11
|
context '.verify_aud(payload, options)' do
|
|
11
12
|
let(:scalar_aud) { 'ruby-jwt-aud' }
|
|
12
|
-
let(:array_aud) { %w
|
|
13
|
+
let(:array_aud) { %w[ruby-jwt-aud test-aud ruby-ruby-ruby] }
|
|
13
14
|
let(:scalar_payload) { base_payload.merge('aud' => scalar_aud) }
|
|
14
15
|
let(:array_payload) { base_payload.merge('aud' => array_aud) }
|
|
15
16
|
|
|
@@ -43,7 +44,6 @@ module JWT
|
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
context '.verify_expiration(payload, options)' do
|
|
46
|
-
let(:leeway) { 10 }
|
|
47
47
|
let(:payload) { base_payload.merge('exp' => (Time.now.to_i - 5)) }
|
|
48
48
|
|
|
49
49
|
it 'must raise JWT::ExpiredSignature when the token has expired' do
|
|
@@ -67,6 +67,16 @@ module JWT
|
|
|
67
67
|
Verify.verify_expiration(payload, options)
|
|
68
68
|
end.to raise_error JWT::ExpiredSignature
|
|
69
69
|
end
|
|
70
|
+
|
|
71
|
+
context 'when leeway is not specified' do
|
|
72
|
+
let(:options) { {} }
|
|
73
|
+
|
|
74
|
+
it 'used a default leeway of 0' do
|
|
75
|
+
expect do
|
|
76
|
+
Verify.verify_expiration(payload, options)
|
|
77
|
+
end.to raise_error JWT::ExpiredSignature
|
|
78
|
+
end
|
|
79
|
+
end
|
|
70
80
|
end
|
|
71
81
|
|
|
72
82
|
context '.verify_iat(payload, options)' do
|
|
@@ -108,20 +118,39 @@ module JWT
|
|
|
108
118
|
|
|
109
119
|
let(:invalid_token) { JWT.encode base_payload, payload[:secret] }
|
|
110
120
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
context 'when iss is a String' do
|
|
122
|
+
it 'must raise JWT::InvalidIssuerError when the configured issuer does not match the payload issuer' do
|
|
123
|
+
expect do
|
|
124
|
+
Verify.verify_iss(payload, options.merge(iss: 'mismatched-issuer'))
|
|
125
|
+
end.to raise_error JWT::InvalidIssuerError
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'must raise JWT::InvalidIssuerError when the payload does not include an issuer' do
|
|
129
|
+
expect do
|
|
130
|
+
Verify.verify_iss(base_payload, options.merge(iss: iss))
|
|
131
|
+
end.to raise_error(JWT::InvalidIssuerError, /received <none>/)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'must allow a matching issuer to pass' do
|
|
135
|
+
Verify.verify_iss(payload, options.merge(iss: iss))
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
context 'when iss is an Array' do
|
|
139
|
+
it 'must raise JWT::InvalidIssuerError when no matching issuers in array' do
|
|
140
|
+
expect do
|
|
141
|
+
Verify.verify_iss(payload, options.merge(iss: %w[first second]))
|
|
142
|
+
end.to raise_error JWT::InvalidIssuerError
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'must raise JWT::InvalidIssuerError when the payload does not include an issuer' do
|
|
146
|
+
expect do
|
|
147
|
+
Verify.verify_iss(base_payload, options.merge(iss: %w[first second]))
|
|
148
|
+
end.to raise_error(JWT::InvalidIssuerError, /received <none>/)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it 'must allow an array with matching issuer to pass' do
|
|
152
|
+
Verify.verify_iss(payload, options.merge(iss: ['first', iss, 'third']))
|
|
153
|
+
end
|
|
125
154
|
end
|
|
126
155
|
end
|
|
127
156
|
|
data/spec/jwt_spec.rb
CHANGED
|
@@ -60,9 +60,17 @@ describe JWT do
|
|
|
60
60
|
JWT.encode payload, nil, alg
|
|
61
61
|
end.to raise_error JWT::InvalidPayload
|
|
62
62
|
end
|
|
63
|
+
|
|
64
|
+
it 'should display a better error message if payload exp is not an Integer' do
|
|
65
|
+
payload['exp'] = Time.now.to_i.to_s
|
|
66
|
+
|
|
67
|
+
expect do
|
|
68
|
+
JWT.encode payload, nil, alg
|
|
69
|
+
end.to raise_error JWT::InvalidPayload
|
|
70
|
+
end
|
|
63
71
|
end
|
|
64
72
|
|
|
65
|
-
%w
|
|
73
|
+
%w[HS256 HS512256 HS384 HS512].each do |alg|
|
|
66
74
|
context "alg: #{alg}" do
|
|
67
75
|
it 'should generate a valid token' do
|
|
68
76
|
token = JWT.encode payload, data[:secret], alg
|
|
@@ -91,7 +99,7 @@ describe JWT do
|
|
|
91
99
|
end
|
|
92
100
|
end
|
|
93
101
|
|
|
94
|
-
%w
|
|
102
|
+
%w[RS256 RS384 RS512].each do |alg|
|
|
95
103
|
context "alg: #{alg}" do
|
|
96
104
|
it 'should generate a valid token' do
|
|
97
105
|
token = JWT.encode payload, data[:rsa_private], alg
|
|
@@ -124,7 +132,7 @@ describe JWT do
|
|
|
124
132
|
end
|
|
125
133
|
end
|
|
126
134
|
|
|
127
|
-
%w
|
|
135
|
+
%w[ES256 ES384 ES512].each do |alg|
|
|
128
136
|
context "alg: #{alg}" do
|
|
129
137
|
before(:each) do
|
|
130
138
|
data[alg] = JWT.encode payload, data["#{alg}_private"], alg
|
|
@@ -230,4 +238,20 @@ describe JWT do
|
|
|
230
238
|
expect(JWT::Encode.base64url_encode('foo')).to eq('string-with_non-url-safe_characters_')
|
|
231
239
|
end
|
|
232
240
|
end
|
|
241
|
+
|
|
242
|
+
it 'should not verify token even if the payload has claims' do
|
|
243
|
+
head = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9'
|
|
244
|
+
load = 'eyJ1c2VyX2lkIjo1NCwiZXhwIjoxNTA0MzkwODA0fQ'
|
|
245
|
+
sign = 'Skpi6FfYMbZ-DwW9ocyRIosNMdPMAIWRLYxRO68GTQk'
|
|
246
|
+
|
|
247
|
+
expect do
|
|
248
|
+
JWT.decode([head, load, sign].join('.'), '', false)
|
|
249
|
+
end.not_to raise_error
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it 'should not raise InvalidPayload exception if payload is an array' do
|
|
253
|
+
expect do
|
|
254
|
+
JWT.encode(['my', 'payload'], 'secret')
|
|
255
|
+
end.not_to raise_error
|
|
256
|
+
end
|
|
233
257
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -18,7 +18,7 @@ CERT_PATH = File.join(File.dirname(__FILE__), 'fixtures', 'certs')
|
|
|
18
18
|
|
|
19
19
|
RSpec.configure do |config|
|
|
20
20
|
config.expect_with :rspec do |c|
|
|
21
|
-
c.syntax = [
|
|
21
|
+
c.syntax = %i[should expect]
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
config.run_all_when_everything_filtered = true
|
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.0.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-09-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -130,7 +130,9 @@ extensions: []
|
|
|
130
130
|
extra_rdoc_files: []
|
|
131
131
|
files:
|
|
132
132
|
- ".codeclimate.yml"
|
|
133
|
+
- ".ebert.yml"
|
|
133
134
|
- ".gitignore"
|
|
135
|
+
- ".reek.yml"
|
|
134
136
|
- ".rspec"
|
|
135
137
|
- ".rubocop.yml"
|
|
136
138
|
- ".travis.yml"
|
|
@@ -145,6 +147,7 @@ files:
|
|
|
145
147
|
- lib/jwt/default_options.rb
|
|
146
148
|
- lib/jwt/encode.rb
|
|
147
149
|
- lib/jwt/error.rb
|
|
150
|
+
- lib/jwt/security_utils.rb
|
|
148
151
|
- lib/jwt/signature.rb
|
|
149
152
|
- lib/jwt/verify.rb
|
|
150
153
|
- lib/jwt/version.rb
|
|
@@ -183,17 +186,17 @@ require_paths:
|
|
|
183
186
|
- lib
|
|
184
187
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
185
188
|
requirements:
|
|
186
|
-
- - "
|
|
189
|
+
- - ">="
|
|
187
190
|
- !ruby/object:Gem::Version
|
|
188
191
|
version: '2.1'
|
|
189
192
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
190
193
|
requirements:
|
|
191
|
-
- - "
|
|
194
|
+
- - ">="
|
|
192
195
|
- !ruby/object:Gem::Version
|
|
193
|
-
version:
|
|
196
|
+
version: '0'
|
|
194
197
|
requirements: []
|
|
195
198
|
rubyforge_project:
|
|
196
|
-
rubygems_version: 2.6.
|
|
199
|
+
rubygems_version: 2.6.13
|
|
197
200
|
signing_key:
|
|
198
201
|
specification_version: 4
|
|
199
202
|
summary: JSON Web Token implementation in Ruby
|