jwt 1.5.1 → 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/.codeclimate.yml +20 -0
- data/.ebert.yml +18 -0
- data/.gitignore +11 -0
- data/.reek.yml +40 -0
- data/.rspec +1 -0
- data/.rubocop.yml +98 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.md +476 -0
- data/Gemfile +3 -0
- data/LICENSE +7 -0
- data/Manifest +3 -1
- data/README.md +478 -0
- data/Rakefile +8 -15
- 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/decode.rb +49 -0
- data/lib/jwt/default_options.rb +15 -0
- data/lib/jwt/encode.rb +51 -0
- data/lib/jwt/error.rb +16 -0
- data/lib/jwt/security_utils.rb +51 -0
- data/lib/jwt/signature.rb +50 -0
- data/lib/jwt/verify.rb +102 -0
- data/lib/jwt/version.rb +24 -0
- data/lib/jwt.rb +33 -203
- data/ruby-jwt.gemspec +31 -0
- data/spec/fixtures/certs/ec256-private.pem +8 -0
- data/spec/fixtures/certs/ec256-public.pem +4 -0
- data/spec/fixtures/certs/ec256-wrong-private.pem +8 -0
- data/spec/fixtures/certs/ec256-wrong-public.pem +4 -0
- data/spec/fixtures/certs/ec384-private.pem +9 -0
- data/spec/fixtures/certs/ec384-public.pem +5 -0
- data/spec/fixtures/certs/ec384-wrong-private.pem +9 -0
- data/spec/fixtures/certs/ec384-wrong-public.pem +5 -0
- data/spec/fixtures/certs/ec512-private.pem +10 -0
- data/spec/fixtures/certs/ec512-public.pem +6 -0
- data/spec/fixtures/certs/ec512-wrong-private.pem +10 -0
- data/spec/fixtures/certs/ec512-wrong-public.pem +6 -0
- data/spec/fixtures/certs/rsa-1024-private.pem +15 -0
- data/spec/fixtures/certs/rsa-1024-public.pem +6 -0
- data/spec/fixtures/certs/rsa-2048-private.pem +27 -0
- data/spec/fixtures/certs/rsa-2048-public.pem +9 -0
- data/spec/fixtures/certs/rsa-2048-wrong-private.pem +27 -0
- data/spec/fixtures/certs/rsa-2048-wrong-public.pem +9 -0
- data/spec/fixtures/certs/rsa-4096-private.pem +51 -0
- data/spec/fixtures/certs/rsa-4096-public.pem +14 -0
- data/spec/integration/readme_examples_spec.rb +202 -0
- data/spec/jwt/verify_spec.rb +232 -0
- data/spec/jwt_spec.rb +236 -384
- data/spec/spec_helper.rb +28 -0
- metadata +187 -26
- data/jwt.gemspec +0 -34
- data/lib/jwt/json.rb +0 -32
- data/spec/helper.rb +0 -19
data/spec/jwt_spec.rb
CHANGED
@@ -1,463 +1,315 @@
|
|
1
|
-
|
2
|
-
require '
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'jwt'
|
3
|
+
require 'jwt/encode'
|
4
|
+
require 'jwt/decode'
|
3
5
|
|
4
6
|
describe JWT do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
7
|
+
let(:payload) { { 'user_id' => 'some@user.tld' } }
|
8
|
+
|
9
|
+
let :data do
|
10
|
+
{
|
11
|
+
:secret => 'My$ecretK3y',
|
12
|
+
:rsa_private => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'rsa-2048-private.pem'))),
|
13
|
+
:rsa_public => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'rsa-2048-public.pem'))),
|
14
|
+
:wrong_rsa_private => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'rsa-2048-wrong-public.pem'))),
|
15
|
+
:wrong_rsa_public => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'rsa-2048-wrong-public.pem'))),
|
16
|
+
'ES256_private' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec256-private.pem'))),
|
17
|
+
'ES256_public' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec256-public.pem'))),
|
18
|
+
'ES384_private' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec384-private.pem'))),
|
19
|
+
'ES384_public' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec384-public.pem'))),
|
20
|
+
'ES512_private' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec512-private.pem'))),
|
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,
|
24
|
+
'NONE' => 'eyJhbGciOiJub25lIn0.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.',
|
25
|
+
'HS256' => 'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.kWOVtIOpWcG7JnyJG0qOkTDbOy636XrrQhMm_8JrRQ8',
|
26
|
+
'HS512256' => 'eyJhbGciOiJIUzUxMjI1NiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.Ds_4ibvf7z4QOBoKntEjDfthy3WJ-3rKMspTEcHE2bA',
|
27
|
+
'HS384' => 'eyJhbGciOiJIUzM4NCJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.VuV4j4A1HKhWxCNzEcwc9qVF3frrEu-BRLzvYPkbWO0LENRGy5dOiBQ34remM3XH',
|
28
|
+
'HS512' => 'eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.8zNtCBTJIZTHpZ-BkhR-6sZY1K85Nm5YCKqV3AxRdsBJDt_RR-REH2db4T3Y0uQwNknhrCnZGvhNHrvhDwV1kA',
|
29
|
+
'RS256' => 'eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.eSXvWP4GViiwUALj_-qTxU68I1oM0XjgDsCZBBUri2Ghh9d75QkVDoZ_v872GaqunN5A5xcnBK0-cOq-CR6OwibgJWfOt69GNzw5RrOfQ2mz3QI3NYEq080nF69h8BeqkiaXhI24Q51joEgfa9aj5Y-oitLAmtDPYTm7vTcdGufd6AwD3_3jajKBwkh0LPSeMtbe_5EyS94nFoEF9OQuhJYjUmp7agsBVa8FFEjVw5jEgVqkvERSj5hSY4nEiCAomdVxIKBfykyi0d12cgjhI7mBFwWkPku8XIPGZ7N8vpiSLdM68BnUqIK5qR7NAhtvT7iyLFgOqhZNUQ6Ret5VpQ',
|
30
|
+
'RS384' => 'eyJhbGciOiJSUzM4NCJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.Sfgk56moPghtsjaP4so6tOy3I553mgwX-5gByMC6dX8lpeWgsxSeAd_K8IyO7u4lwYOL0DSftnqO1HEOuN1AKyBbDvaTXz3u2xNA2x4NYLdW4AZA6ritbYcKLO5BHTXw5ueMbtA1jjGXP0zI_aK2iJTMBmB8SCF88RYBUH01Tyf4PlLj98pGL-v3prZd6kZkIeRJ3326h04hslcB5HQKmgeBk24QNLIoIC-CD329HPjJ7TtGx01lj-ehTBnwVbBGzYFAyoalV5KgvL_MDOfWPr1OYHnR5s_Fm6_3Vg4u6lBljvHOrmv4Nfx7d8HLgbo8CwH4qn1wm6VQCtuDd-uhRg',
|
31
|
+
'RS512' => 'eyJhbGciOiJSUzUxMiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.LIIAUEuCkGNdpYguOO5LoW4rZ7ED2POJrB0pmEAAchyTdIK4HKh1jcLxc6KyGwZv40njCgub3y72q6vcQTn7oD0zWFCVQRIDW1911Ii2hRNHuigiPUnrnZh1OQ6z65VZRU6GKs8omoBGU9vrClBU0ODqYE16KxYmE_0n4Xw2h3D_L1LF0IAOtDWKBRDa3QHwZRM9sHsHNsBuD5ye9KzDYN1YALXj64LBfA-DoCKfpVAm9NkRPOyzjR2X2C3TomOSJgqWIVHJucudKDDAZyEbO4RA5pI-UFYy1370p9bRajvtDyoBuLDCzoSkMyQ4L2DnLhx5CbWcnD7Cd3GUmnjjTA',
|
32
|
+
'ES256' => '',
|
33
|
+
'ES384' => '',
|
34
|
+
'ES512' => ''
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
after(:each) do
|
39
|
+
expect(OpenSSL.errors).to be_empty
|
31
40
|
end
|
32
41
|
|
33
|
-
|
34
|
-
|
35
|
-
private_key.generate_key
|
36
|
-
public_key = OpenSSL::PKey::EC.new(private_key)
|
37
|
-
public_key.private_key = nil
|
38
|
-
jwt = JWT.encode(@payload, private_key, 'ES384')
|
39
|
-
decoded_payload = JWT.decode(jwt, public_key)
|
40
|
-
expect(decoded_payload).to include(@payload)
|
41
|
-
end
|
42
|
+
context 'alg: NONE' do
|
43
|
+
let(:alg) { 'none' }
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
private_key.generate_key
|
46
|
-
public_key = OpenSSL::PKey::EC.new(private_key)
|
47
|
-
public_key.private_key = nil
|
48
|
-
jwt = JWT.encode(@payload, private_key, 'ES512')
|
49
|
-
decoded_payload = JWT.decode(jwt, public_key)
|
50
|
-
expect(decoded_payload).to include(@payload)
|
51
|
-
end
|
45
|
+
it 'should generate a valid token' do
|
46
|
+
token = JWT.encode payload, nil, alg
|
52
47
|
|
53
|
-
|
54
|
-
private_key = OpenSSL::PKey::RSA.generate(512)
|
55
|
-
jwt = JWT.encode(@payload, private_key, 'RS256', 'kid' => 'default')
|
56
|
-
decoded_payload = JWT.decode(jwt) do |header|
|
57
|
-
expect(header['kid']).to eq('default')
|
58
|
-
private_key.public_key
|
48
|
+
expect(token).to eq data['NONE']
|
59
49
|
end
|
60
|
-
expect(decoded_payload).to include(@payload)
|
61
|
-
end
|
62
50
|
|
63
|
-
|
64
|
-
|
65
|
-
private_key.generate_key
|
66
|
-
expect do
|
67
|
-
JWT.encode(@payload, private_key, 'ES512')
|
68
|
-
end.to raise_error(JWT::IncorrectAlgorithm, 'payload algorithm is ES512 but ES256 signing key was provided')
|
69
|
-
end
|
51
|
+
it 'should decode a valid token' do
|
52
|
+
jwt_payload, header = JWT.decode data['NONE'], nil, false
|
70
53
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
example_jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8'
|
75
|
-
decoded_payload = JWT.decode(example_jwt, example_secret)
|
76
|
-
expect(decoded_payload).to include(example_payload)
|
77
|
-
end
|
54
|
+
expect(header['alg']).to eq alg
|
55
|
+
expect(jwt_payload).to eq payload
|
56
|
+
end
|
78
57
|
|
79
|
-
|
80
|
-
|
81
|
-
example_jwt = 'eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJoZWxsbyI6IndvcmxkIn0.AQx1MqdTni6KuzfOoedg2-7NUiwe-b88SWbdmviz40GTwrM0Mybp1i1tVtmTSQ91oEXGXBdtwsN6yalzP9J-sp2YATX_Tv4h-BednbdSvYxZsYnUoZ--ZUdL10t7g8Yt3y9hdY_diOjIptcha6ajX8yzkDGYG42iSe3f5LywSuD6FO5c'
|
82
|
-
pubkey_pem = "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAcpkss6wI7PPlxj3t7A1RqMH3nvL4\nL5Tzxze/XeeYZnHqxiX+gle70DlGRMqqOq+PJ6RYX7vK0PJFdiAIXlyPQq0B3KaU\ne86IvFeQSFrJdCc0K8NfiH2G1loIk3fiR+YLqlXk6FAeKtpXJKxR1pCQCAM+vBCs\nmZudf1zCUZ8/4eodlHU=\n-----END PUBLIC KEY-----"
|
83
|
-
pubkey = OpenSSL::PKey::EC.new pubkey_pem
|
84
|
-
decoded_payload = JWT.decode(example_jwt, pubkey)
|
85
|
-
expect(decoded_payload).to include(example_payload)
|
86
|
-
end
|
58
|
+
it 'should display a better error message if payload exp is_a?(Time)' do
|
59
|
+
payload['exp'] = Time.now
|
87
60
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
decoded_payload = JWT.decode(example_jwt, example_secret, true, 'iss' => 'jwtiss')
|
93
|
-
expect(decoded_payload).to include(example_payload)
|
94
|
-
end
|
61
|
+
expect do
|
62
|
+
JWT.encode payload, nil, alg
|
63
|
+
end.to raise_error JWT::InvalidPayload
|
64
|
+
end
|
95
65
|
|
96
|
-
|
97
|
-
|
98
|
-
example_secret = 'secret'
|
66
|
+
it 'should display a better error message if payload exp is not an Integer' do
|
67
|
+
payload['exp'] = Time.now.to_i.to_s
|
99
68
|
|
100
|
-
|
101
|
-
|
69
|
+
expect do
|
70
|
+
JWT.encode payload, nil, alg
|
71
|
+
end.to raise_error JWT::InvalidPayload
|
102
72
|
end
|
73
|
+
end
|
103
74
|
|
104
|
-
|
105
|
-
|
75
|
+
%w[HS256 HS512256 HS384 HS512].each do |alg|
|
76
|
+
context "alg: #{alg}" do
|
77
|
+
it 'should generate a valid token' do
|
78
|
+
token = JWT.encode payload, data[:secret], alg
|
106
79
|
|
107
|
-
|
108
|
-
|
109
|
-
end
|
80
|
+
expect(token).to eq data[alg]
|
81
|
+
end
|
110
82
|
|
111
|
-
|
112
|
-
|
83
|
+
it 'should decode a valid token' do
|
84
|
+
jwt_payload, header = JWT.decode data[alg], data[:secret], true, algorithm: alg
|
113
85
|
|
114
|
-
|
115
|
-
|
116
|
-
|
86
|
+
expect(header['alg']).to eq alg
|
87
|
+
expect(jwt_payload).to eq payload
|
88
|
+
end
|
117
89
|
|
118
|
-
|
119
|
-
|
90
|
+
it 'wrong secret should raise JWT::DecodeError' do
|
91
|
+
expect do
|
92
|
+
JWT.decode data[alg], 'wrong_secret', true, algorithm: alg
|
93
|
+
end.to raise_error JWT::VerificationError
|
94
|
+
end
|
120
95
|
|
121
|
-
|
122
|
-
|
96
|
+
it 'wrong secret and verify = false should not raise JWT::DecodeError' do
|
97
|
+
expect do
|
98
|
+
JWT.decode data[alg], 'wrong_secret', false
|
99
|
+
end.not_to raise_error
|
100
|
+
end
|
123
101
|
end
|
124
102
|
end
|
125
103
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
decoded_payload = JWT.decode(example_jwt, example_secret, true, 'iat' => true)
|
131
|
-
expect(decoded_payload).to include(example_payload)
|
132
|
-
end
|
104
|
+
%w[RS256 RS384 RS512].each do |alg|
|
105
|
+
context "alg: #{alg}" do
|
106
|
+
it 'should generate a valid token' do
|
107
|
+
token = JWT.encode payload, data[:rsa_private], alg
|
133
108
|
|
134
|
-
|
135
|
-
|
136
|
-
example_secret = 'secret'
|
137
|
-
example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaWF0IjoiMTQyNTkxNzIwOSJ9.Mn_vk61xWjIhbXFqAB0nFmNkDiCmfzUgl_LaCKRT6S8'
|
138
|
-
expect { JWT.decode(example_jwt, example_secret, true, :verify_iat => true, 'iat' => 1425917209) }.to raise_error(JWT::InvalidIatError)
|
139
|
-
end
|
109
|
+
expect(token).to eq data[alg]
|
110
|
+
end
|
140
111
|
|
141
|
-
|
142
|
-
|
143
|
-
example_secret = 'secret'
|
144
|
-
example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaWF0IjoxNDI1OTE3MjA5LCJqdGkiOiI1NWM3NzZlMjFmN2NiZDg3OWMwNmZhYzAxOGRhYzQwMiJ9.ET0hb-VTUOL3M22oG13ofzvGPLMAncbF8rdNDIqo8tg'
|
145
|
-
decoded_payload = JWT.decode(example_jwt, example_secret, true, 'jti' => Digest::MD5.hexdigest('secret:1425917209'))
|
146
|
-
expect(decoded_payload).to include(example_payload)
|
147
|
-
end
|
112
|
+
it 'should decode a valid token' do
|
113
|
+
jwt_payload, header = JWT.decode data[alg], data[:rsa_public], true, algorithm: alg
|
148
114
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaWF0IjoxNDI1OTE3MjA5LCJqdGkiOiI1NWM3NzZlMjFmN2NiZDg3OWMwNmZhYzAxOGRhYzQwMiJ9.ET0hb-VTUOL3M22oG13ofzvGPLMAncbF8rdNDIqo8tg'
|
153
|
-
expect { JWT.decode(example_jwt, example_secret, true, :verify_jti => true, 'jti' => Digest::MD5.hexdigest('secret:1425922032')) }.to raise_error(JWT::InvalidJtiError)
|
154
|
-
# expect{ JWT.decode(example_jwt, example_secret) }.to raise_error(JWT::InvalidJtiError)
|
155
|
-
end
|
115
|
+
expect(header['alg']).to eq alg
|
116
|
+
expect(jwt_payload).to eq payload
|
117
|
+
end
|
156
118
|
|
157
|
-
|
158
|
-
|
159
|
-
example_secret = 'secret'
|
160
|
-
example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwianRpIjoiNTVjNzc2ZTIxZjdjYmQ4NzljMDZmYWMwMThkYWM0MDIifQ.n0foJCnCM_-_xUvG_TOmR9mYpL2y0UqZOD_gv33djeE'
|
161
|
-
expect { JWT.decode(example_jwt, example_secret, true, :verify_jti => true, 'jti' => Digest::MD5.hexdigest('secret:1425922032')) }.to raise_error(JWT::InvalidJtiError)
|
162
|
-
end
|
119
|
+
it 'wrong key should raise JWT::DecodeError' do
|
120
|
+
key = OpenSSL::PKey.read File.read(File.join(CERT_PATH, 'rsa-2048-wrong-public.pem'))
|
163
121
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
example_secret = 'secret'
|
169
|
-
example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiYXVkIjoidXJsOnBuZCJ9._gT5veUtNiZD7wLEC6Gd0-nkQV3cl1z8G0zXq8qcd-8'
|
170
|
-
example_jwt2 = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiYXVkIjpbInVybDpwbmQiLCJhdWQ6eWVzIl19.qNPNcT4X9B5uI91rIwbW2bIPTsp8wbRYW3jkZkrmqbQ'
|
171
|
-
decoded_payload = JWT.decode(example_jwt, example_secret, true, :verify_aud => true, 'aud' => 'url:pnd')
|
172
|
-
decoded_payload2 = JWT.decode(example_jwt2, example_secret, true, :verify_aud => true, 'aud' => 'url:pnd')
|
173
|
-
expect(decoded_payload).to include(example_payload)
|
174
|
-
expect(decoded_payload2).to include(example_payload2)
|
175
|
-
end
|
122
|
+
expect do
|
123
|
+
JWT.decode data[alg], key, true, algorithm: alg
|
124
|
+
end.to raise_error JWT::DecodeError
|
125
|
+
end
|
176
126
|
|
177
|
-
|
178
|
-
|
179
|
-
example_secret = 'secret'
|
180
|
-
example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiYXVkIjoidXJsOnBuZCJ9._gT5veUtNiZD7wLEC6Gd0-nkQV3cl1z8G0zXq8qcd-8'
|
181
|
-
expect { JWT.decode(example_jwt, example_secret, true, :verify_aud => true, 'aud' => 'wrong:aud') }.to raise_error(JWT::InvalidAudError)
|
182
|
-
end
|
127
|
+
it 'wrong key and verify = false should not raise JWT::DecodeError' do
|
128
|
+
key = OpenSSL::PKey.read File.read(File.join(CERT_PATH, 'rsa-2048-wrong-public.pem'))
|
183
129
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
expect { JWT.decode(example_jwt, example_secret, true, :verify_aud => true, 'aud' => 'url:pnd') }.to raise_error(JWT::InvalidAudError)
|
130
|
+
expect do
|
131
|
+
JWT.decode data[alg], key, false
|
132
|
+
end.not_to raise_error
|
133
|
+
end
|
189
134
|
end
|
190
135
|
end
|
191
136
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
expect(decoded_payload).to include(example_payload)
|
198
|
-
end
|
199
|
-
|
200
|
-
it 'raise decode exception when the sub is invalid'
|
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
|
201
142
|
|
202
|
-
|
203
|
-
example_secret = 'secret'
|
204
|
-
# Same as above exmaple with some random bytes replaced
|
205
|
-
example_jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHiMomlwIjogIkJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8'
|
206
|
-
expect { JWT.decode(example_jwt, example_secret) }.to raise_error(JWT::DecodeError)
|
207
|
-
end
|
143
|
+
let(:wrong_key) { OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec256-wrong-public.pem'))) }
|
208
144
|
|
209
|
-
|
210
|
-
|
211
|
-
bad_secret = 'bar'
|
212
|
-
jwt_message = JWT.encode(@payload, right_secret, 'HS256')
|
213
|
-
expect { JWT.decode(jwt_message, bad_secret) }.to raise_error(JWT::VerificationError)
|
214
|
-
end
|
145
|
+
it 'should generate a valid token' do
|
146
|
+
jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
|
215
147
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
right_public_key = OpenSSL::PKey::EC.new(right_private_key)
|
220
|
-
right_public_key.private_key = nil
|
221
|
-
bad_private_key = OpenSSL::PKey::EC.new('secp384r1')
|
222
|
-
bad_private_key.generate_key
|
223
|
-
bad_public_key = OpenSSL::PKey::EC.new(bad_private_key)
|
224
|
-
bad_public_key.private_key = nil
|
225
|
-
jwt = JWT.encode(@payload, right_private_key, 'ES256')
|
226
|
-
expect do
|
227
|
-
JWT.decode(jwt, bad_public_key)
|
228
|
-
end.to raise_error(JWT::IncorrectAlgorithm, 'payload algorithm is ES256 but ES384 verification key was provided')
|
229
|
-
end
|
230
|
-
|
231
|
-
it 'raises verification exception with wrong rsa key' do
|
232
|
-
right_private_key = OpenSSL::PKey::RSA.generate(512)
|
233
|
-
bad_private_key = OpenSSL::PKey::RSA.generate(512)
|
234
|
-
jwt = JWT.encode(@payload, right_private_key, 'RS256')
|
235
|
-
expect { JWT.decode(jwt, bad_private_key.public_key) }.to raise_error(JWT::VerificationError)
|
236
|
-
end
|
148
|
+
expect(header['alg']).to eq alg
|
149
|
+
expect(jwt_payload).to eq payload
|
150
|
+
end
|
237
151
|
|
238
|
-
|
239
|
-
|
240
|
-
right_private_key.generate_key
|
241
|
-
bad_private_key = OpenSSL::PKey::EC.new('prime256v1')
|
242
|
-
bad_private_key.generate_key
|
243
|
-
bad_public_key = OpenSSL::PKey::EC.new(bad_private_key)
|
244
|
-
bad_public_key.private_key = nil
|
245
|
-
jwt = JWT.encode(@payload, right_private_key, 'ES256')
|
246
|
-
expect { JWT.decode(jwt, bad_public_key) }.to raise_error(JWT::VerificationError)
|
247
|
-
end
|
152
|
+
it 'should decode a valid token' do
|
153
|
+
jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
|
248
154
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
expect { JWT.decode(example_jwt, example_secret) }.to raise_error(JWT::DecodeError)
|
253
|
-
end
|
155
|
+
expect(header['alg']).to eq alg
|
156
|
+
expect(jwt_payload).to eq payload
|
157
|
+
end
|
254
158
|
|
255
|
-
|
256
|
-
|
257
|
-
|
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
|
258
164
|
|
259
|
-
|
260
|
-
|
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
|
261
171
|
end
|
172
|
+
%w[ES256 ES384 ES512].each do |alg|
|
173
|
+
context "alg: #{alg}" do
|
174
|
+
before(:each) do
|
175
|
+
data[alg] = JWT.encode payload, data["#{alg}_private"], alg
|
176
|
+
end
|
262
177
|
|
263
|
-
|
264
|
-
expect { JWT.decode(nil) }.to raise_error(JWT::DecodeError)
|
265
|
-
end
|
178
|
+
let(:wrong_key) { OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec256-wrong-public.pem'))) }
|
266
179
|
|
267
|
-
|
268
|
-
|
269
|
-
bad_secret = 'bar'
|
270
|
-
jwt = JWT.encode(@payload, right_secret)
|
271
|
-
decoded_payload = JWT.decode(jwt, bad_secret, false)
|
272
|
-
expect(decoded_payload).to include(@payload)
|
273
|
-
end
|
180
|
+
it 'should generate a valid token' do
|
181
|
+
jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
|
274
182
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
jwt = JWT.encode(@payload, right_secret)
|
279
|
-
verify = 'yes' =~ /^y/i
|
280
|
-
expect { JWT.decode(jwt, bad_secret, verify) }.to raise_error(JWT::DecodeError)
|
281
|
-
end
|
183
|
+
expect(header['alg']).to eq alg
|
184
|
+
expect(jwt_payload).to eq payload
|
185
|
+
end
|
282
186
|
|
283
|
-
|
284
|
-
|
285
|
-
end
|
187
|
+
it 'should decode a valid token' do
|
188
|
+
jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
|
286
189
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
end
|
190
|
+
expect(header['alg']).to eq alg
|
191
|
+
expect(jwt_payload).to eq payload
|
192
|
+
end
|
291
193
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
194
|
+
it 'wrong key should raise JWT::DecodeError' do
|
195
|
+
expect do
|
196
|
+
JWT.decode data[alg], wrong_key
|
197
|
+
end.to raise_error JWT::DecodeError
|
198
|
+
end
|
296
199
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
200
|
+
it 'wrong key and verify = false should not raise JWT::DecodeError' do
|
201
|
+
expect do
|
202
|
+
JWT.decode data[alg], wrong_key, false
|
203
|
+
end.not_to raise_error
|
204
|
+
end
|
205
|
+
end
|
302
206
|
end
|
303
207
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
208
|
+
context 'Invalid' do
|
209
|
+
it 'algorithm should raise NotImplementedError' do
|
210
|
+
expect do
|
211
|
+
JWT.encode payload, 'secret', 'HS255'
|
212
|
+
end.to raise_error NotImplementedError
|
213
|
+
end
|
309
214
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
crypto_segment = jwt.split('.').last
|
215
|
+
it 'ECDSA curve_name should raise JWT::IncorrectAlgorithm' do
|
216
|
+
key = OpenSSL::PKey::EC.new 'secp256k1'
|
217
|
+
key.generate_key
|
314
218
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
expect(JWT).to receive(:base64url_decode).at_least(:once).and_call_original
|
219
|
+
expect do
|
220
|
+
JWT.encode payload, key, 'ES256'
|
221
|
+
end.to raise_error JWT::IncorrectAlgorithm
|
319
222
|
|
320
|
-
|
321
|
-
|
223
|
+
token = JWT.encode payload, data['ES256_private'], 'ES256'
|
224
|
+
key.private_key = nil
|
322
225
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
jwt = JWT.encode(expired_payload, secret)
|
328
|
-
expect { JWT.decode(jwt, secret) }.to raise_error(JWT::ExpiredSignature)
|
226
|
+
expect do
|
227
|
+
JWT.decode token, key
|
228
|
+
end.to raise_error JWT::IncorrectAlgorithm
|
229
|
+
end
|
329
230
|
end
|
330
231
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
jwt = JWT.encode(expired_payload, secret)
|
336
|
-
expect { JWT.decode(jwt, secret) }.to raise_error(JWT::ExpiredSignature)
|
337
|
-
end
|
232
|
+
context 'Verify' do
|
233
|
+
context 'algorithm' do
|
234
|
+
it 'should raise JWT::IncorrectAlgorithm on mismatch' do
|
235
|
+
token = JWT.encode payload, data[:secret], 'HS256'
|
338
236
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
secret = 'secret'
|
343
|
-
jwt = JWT.encode(expired_payload, secret)
|
344
|
-
decoded_payload = JWT.decode(jwt, secret, true, :verify_expiration => false)
|
345
|
-
expect(decoded_payload).to include(expired_payload)
|
346
|
-
end
|
237
|
+
expect do
|
238
|
+
JWT.decode token, data[:secret], true, algorithm: 'HS384'
|
239
|
+
end.to raise_error JWT::IncorrectAlgorithm
|
347
240
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
jwt = JWT.encode(expired_payload, secret)
|
353
|
-
decoded_payload = JWT.decode(jwt, secret, true, :leeway => 3)
|
354
|
-
expect(decoded_payload).to include(expired_payload)
|
355
|
-
end
|
241
|
+
expect do
|
242
|
+
JWT.decode token, data[:secret], true, algorithm: 'HS256'
|
243
|
+
end.not_to raise_error
|
244
|
+
end
|
356
245
|
|
357
|
-
|
358
|
-
|
359
|
-
immature_payload['nbf'] = Time.now.to_i + 1
|
360
|
-
secret = 'secret'
|
361
|
-
jwt = JWT.encode(immature_payload, secret)
|
362
|
-
expect { JWT.decode(jwt, secret) }.to raise_error(JWT::ImmatureSignature)
|
363
|
-
end
|
246
|
+
it 'should raise JWT::IncorrectAlgorithm when algorithms array does not contain algorithm' do
|
247
|
+
token = JWT.encode payload, data[:secret], 'HS512'
|
364
248
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
jwt = JWT.encode(mature_payload, secret)
|
369
|
-
decoded_payload = JWT.decode(jwt, secret, true, :verify_expiration => false)
|
370
|
-
expect(decoded_payload).to include(mature_payload)
|
371
|
-
end
|
249
|
+
expect do
|
250
|
+
JWT.decode token, data[:secret], true, algorithms: ['HS384']
|
251
|
+
end.to raise_error JWT::IncorrectAlgorithm
|
372
252
|
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
jwt = JWT.encode(immature_payload, secret)
|
378
|
-
expect { JWT.decode(jwt, secret) }.to raise_error(JWT::ImmatureSignature)
|
379
|
-
end
|
253
|
+
expect do
|
254
|
+
JWT.decode token, data[:secret], true, algorithms: ['HS512', 'HS384']
|
255
|
+
end.not_to raise_error
|
256
|
+
end
|
380
257
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
secret = 'secret'
|
385
|
-
jwt = JWT.encode(immature_payload, secret)
|
386
|
-
decoded_payload = JWT.decode(jwt, secret, true, :verify_not_before => false)
|
387
|
-
expect(decoded_payload).to include(immature_payload)
|
388
|
-
end
|
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
|
389
261
|
|
390
|
-
|
391
|
-
immature_payload = @payload.clone
|
392
|
-
immature_payload['nbf'] = Time.now.to_i - 2
|
393
|
-
secret = 'secret'
|
394
|
-
jwt = JWT.encode(immature_payload, secret)
|
395
|
-
decoded_payload = JWT.decode(jwt, secret, true, :leeway => 3)
|
396
|
-
expect(decoded_payload).to include(immature_payload)
|
397
|
-
end
|
262
|
+
jwt_payload, header = JWT.decode token, data[:rsa_public].to_s
|
398
263
|
|
399
|
-
|
400
|
-
|
401
|
-
|
264
|
+
expect(header['alg']).to eq 'HS256'
|
265
|
+
expect(jwt_payload).to eq payload
|
266
|
+
end
|
267
|
+
end
|
402
268
|
end
|
403
269
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
270
|
+
context 'issuer claim' do
|
271
|
+
let(:iss) { 'ruby-jwt-gem' }
|
272
|
+
let(:invalid_token) { JWT.encode payload, data[:secret] }
|
273
|
+
|
274
|
+
let :token do
|
275
|
+
iss_payload = payload.merge(iss: iss)
|
276
|
+
JWT.encode iss_payload, data[:secret]
|
408
277
|
end
|
409
|
-
end
|
410
278
|
|
411
|
-
|
412
|
-
|
279
|
+
it 'if verify_iss is set to false (default option) should not raise JWT::InvalidIssuerError' do
|
280
|
+
expect do
|
281
|
+
JWT.decode token, data[:secret], true, iss: iss, algorithm: 'HS256'
|
282
|
+
end.not_to raise_error
|
283
|
+
end
|
413
284
|
end
|
414
285
|
end
|
415
286
|
|
416
|
-
|
417
|
-
|
418
|
-
|
287
|
+
context 'Base64' do
|
288
|
+
it 'urlsafe replace + / with - _' do
|
289
|
+
allow(Base64).to receive(:encode64) { 'string+with/non+url-safe/characters_' }
|
290
|
+
expect(JWT::Encode.base64url_encode('foo')).to eq('string-with_non-url-safe_characters_')
|
291
|
+
end
|
419
292
|
end
|
420
293
|
|
421
|
-
it '
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
LDZZNliPhfENuKeC2MCGVXTEu8Cqhy1w6e4axavLlXoYf4laJIZ/e7au8SqDbY0B
|
430
|
-
xwIDAQAB
|
431
|
-
-----END PUBLIC KEY-----
|
432
|
-
PUBKEY
|
433
|
-
jwt = (
|
434
|
-
'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiY' \
|
435
|
-
'XVkIjoiMTA2MDM1Nzg5MTY4OC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSI' \
|
436
|
-
'sImNpZCI6IjEwNjAzNTc4OTE2ODguYXBwcy5nb29nbGV1c2VyY29udGVudC5jb' \
|
437
|
-
'20iLCJpZCI6IjExNjQ1MjgyNDMwOTg1Njc4MjE2MyIsInRva2VuX2hhc2giOiJ' \
|
438
|
-
'0Z2hEOUo4bjhWME4ydmN3NmVNaWpnIiwiaWF0IjoxMzIwNjcwOTc4LCJleHAiO' \
|
439
|
-
'jEzMjA2NzQ4Nzh9.D8x_wirkxDElqKdJBcsIws3Ogesk38okz6MN7zqC7nEAA7' \
|
440
|
-
'wcy1PxsROY1fmBvXSer0IQesAqOW-rPOCNReSn-eY8d53ph1x2HAF-AzEi3GOl' \
|
441
|
-
'6hFycH8wj7Su6JqqyEbIVLxE7q7DkAZGaMPkxbTHs1EhSd5_oaKQ6O4xO3ZnnT4'
|
442
|
-
)
|
443
|
-
expect { JWT.decode(jwt, pubkey, true) }.to raise_error(JWT::DecodeError)
|
294
|
+
it 'should not verify token even if the payload has claims' do
|
295
|
+
head = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9'
|
296
|
+
load = 'eyJ1c2VyX2lkIjo1NCwiZXhwIjoxNTA0MzkwODA0fQ'
|
297
|
+
sign = 'Skpi6FfYMbZ-DwW9ocyRIosNMdPMAIWRLYxRO68GTQk'
|
298
|
+
|
299
|
+
expect do
|
300
|
+
JWT.decode([head, load, sign].join('.'), '', false)
|
301
|
+
end.not_to raise_error
|
444
302
|
end
|
445
303
|
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
end
|
304
|
+
it 'should not raise InvalidPayload exception if payload is an array' do
|
305
|
+
expect do
|
306
|
+
JWT.encode(['my', 'payload'], 'secret')
|
307
|
+
end.not_to raise_error
|
451
308
|
end
|
452
309
|
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
decoded_segments = JWT.decoded_segments(jwt)
|
458
|
-
expect(decoded_segments.size).to eq(4)
|
459
|
-
expect(decoded_segments[0]).to eq('typ' => 'JWT', 'alg' => 'HS256')
|
460
|
-
expect(decoded_segments[1]).to eq(@payload)
|
461
|
-
end
|
310
|
+
it 'should encode string payloads' do
|
311
|
+
expect do
|
312
|
+
JWT.encode 'Hello World', 'secret'
|
313
|
+
end.not_to raise_error
|
462
314
|
end
|
463
315
|
end
|