jwt 2.1.0 → 2.2.3

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.
Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yml +74 -0
  3. data/.gitignore +1 -1
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +15 -16
  6. data/.rubocop_todo.yml +191 -0
  7. data/{.ebert.yml → .sourcelevel.yml} +1 -1
  8. data/AUTHORS +101 -0
  9. data/Appraisals +10 -0
  10. data/CHANGELOG.md +247 -19
  11. data/Gemfile +2 -0
  12. data/README.md +154 -89
  13. data/Rakefile +4 -1
  14. data/lib/jwt.rb +9 -42
  15. data/lib/jwt/algos.rb +44 -0
  16. data/lib/jwt/algos/ecdsa.rb +1 -1
  17. data/lib/jwt/algos/hmac.rb +1 -0
  18. data/lib/jwt/algos/none.rb +15 -0
  19. data/lib/jwt/algos/ps.rb +43 -0
  20. data/lib/jwt/algos/unsupported.rb +5 -4
  21. data/lib/jwt/base64.rb +19 -0
  22. data/lib/jwt/claims_validator.rb +35 -0
  23. data/lib/jwt/decode.rb +85 -25
  24. data/lib/jwt/encode.rb +43 -25
  25. data/lib/jwt/error.rb +4 -0
  26. data/lib/jwt/json.rb +18 -0
  27. data/lib/jwt/jwk.rb +51 -0
  28. data/lib/jwt/jwk/ec.rb +150 -0
  29. data/lib/jwt/jwk/hmac.rb +58 -0
  30. data/lib/jwt/jwk/key_base.rb +18 -0
  31. data/lib/jwt/jwk/key_finder.rb +62 -0
  32. data/lib/jwt/jwk/rsa.rb +115 -0
  33. data/lib/jwt/security_utils.rb +6 -0
  34. data/lib/jwt/signature.rb +9 -20
  35. data/lib/jwt/verify.rb +1 -5
  36. data/lib/jwt/version.rb +2 -2
  37. data/ruby-jwt.gemspec +4 -7
  38. metadata +30 -109
  39. data/.codeclimate.yml +0 -20
  40. data/.reek.yml +0 -40
  41. data/.travis.yml +0 -14
  42. data/Manifest +0 -8
  43. data/spec/fixtures/certs/ec256-private.pem +0 -8
  44. data/spec/fixtures/certs/ec256-public.pem +0 -4
  45. data/spec/fixtures/certs/ec256-wrong-private.pem +0 -8
  46. data/spec/fixtures/certs/ec256-wrong-public.pem +0 -4
  47. data/spec/fixtures/certs/ec384-private.pem +0 -9
  48. data/spec/fixtures/certs/ec384-public.pem +0 -5
  49. data/spec/fixtures/certs/ec384-wrong-private.pem +0 -9
  50. data/spec/fixtures/certs/ec384-wrong-public.pem +0 -5
  51. data/spec/fixtures/certs/ec512-private.pem +0 -10
  52. data/spec/fixtures/certs/ec512-public.pem +0 -6
  53. data/spec/fixtures/certs/ec512-wrong-private.pem +0 -10
  54. data/spec/fixtures/certs/ec512-wrong-public.pem +0 -6
  55. data/spec/fixtures/certs/rsa-1024-private.pem +0 -15
  56. data/spec/fixtures/certs/rsa-1024-public.pem +0 -6
  57. data/spec/fixtures/certs/rsa-2048-private.pem +0 -27
  58. data/spec/fixtures/certs/rsa-2048-public.pem +0 -9
  59. data/spec/fixtures/certs/rsa-2048-wrong-private.pem +0 -27
  60. data/spec/fixtures/certs/rsa-2048-wrong-public.pem +0 -9
  61. data/spec/fixtures/certs/rsa-4096-private.pem +0 -51
  62. data/spec/fixtures/certs/rsa-4096-public.pem +0 -14
  63. data/spec/integration/readme_examples_spec.rb +0 -202
  64. data/spec/jwt/verify_spec.rb +0 -232
  65. data/spec/jwt_spec.rb +0 -315
  66. data/spec/spec_helper.rb +0 -28
@@ -1,232 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'jwt/verify'
5
-
6
- module JWT
7
- RSpec.describe Verify do
8
- let(:base_payload) { { 'user_id' => 'some@user.tld' } }
9
- let(:options) { { leeway: 0 } }
10
-
11
- context '.verify_aud(payload, options)' do
12
- let(:scalar_aud) { 'ruby-jwt-aud' }
13
- let(:array_aud) { %w[ruby-jwt-aud test-aud ruby-ruby-ruby] }
14
- let(:scalar_payload) { base_payload.merge('aud' => scalar_aud) }
15
- let(:array_payload) { base_payload.merge('aud' => array_aud) }
16
-
17
- it 'must raise JWT::InvalidAudError when the singular audience does not match' do
18
- expect do
19
- Verify.verify_aud(scalar_payload, options.merge(aud: 'no-match'))
20
- end.to raise_error JWT::InvalidAudError
21
- end
22
-
23
- it 'must raise JWT::InvalidAudError when the payload has an array and none match the supplied value' do
24
- expect do
25
- Verify.verify_aud(array_payload, options.merge(aud: 'no-match'))
26
- end.to raise_error JWT::InvalidAudError
27
- end
28
-
29
- it 'must allow a matching singular audience to pass' do
30
- Verify.verify_aud(scalar_payload, options.merge(aud: scalar_aud))
31
- end
32
-
33
- it 'must allow an array with any value matching the one in the options' do
34
- Verify.verify_aud(array_payload, options.merge(aud: array_aud.first))
35
- end
36
-
37
- it 'must allow an array with any value matching any value in the options array' do
38
- Verify.verify_aud(array_payload, options.merge(aud: array_aud))
39
- end
40
-
41
- it 'must allow a singular audience payload matching any value in the options array' do
42
- Verify.verify_aud(scalar_payload, options.merge(aud: array_aud))
43
- end
44
- end
45
-
46
- context '.verify_expiration(payload, options)' do
47
- let(:payload) { base_payload.merge('exp' => (Time.now.to_i - 5)) }
48
-
49
- it 'must raise JWT::ExpiredSignature when the token has expired' do
50
- expect do
51
- Verify.verify_expiration(payload, options)
52
- end.to raise_error JWT::ExpiredSignature
53
- end
54
-
55
- it 'must allow some leeway in the expiration when global leeway is configured' do
56
- Verify.verify_expiration(payload, options.merge(leeway: 10))
57
- end
58
-
59
- it 'must allow some leeway in the expiration when exp_leeway is configured' do
60
- Verify.verify_expiration(payload, options.merge(exp_leeway: 10))
61
- end
62
-
63
- it 'must be expired if the exp claim equals the current time' do
64
- payload['exp'] = Time.now.to_i
65
-
66
- expect do
67
- Verify.verify_expiration(payload, options)
68
- end.to raise_error JWT::ExpiredSignature
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
80
- end
81
-
82
- context '.verify_iat(payload, options)' do
83
- let(:iat) { Time.now.to_f }
84
- let(:payload) { base_payload.merge('iat' => iat) }
85
-
86
- it 'must allow a valid iat' do
87
- Verify.verify_iat(payload, options)
88
- end
89
-
90
- it 'must allow configured leeway' do
91
- Verify.verify_iat(payload.merge('iat' => (iat + 60)), options.merge(leeway: 70))
92
- end
93
-
94
- it 'must allow configured iat_leeway' do
95
- Verify.verify_iat(payload.merge('iat' => (iat + 60)), options.merge(iat_leeway: 70))
96
- end
97
-
98
- it 'must properly handle integer times' do
99
- Verify.verify_iat(payload.merge('iat' => Time.now.to_i), options)
100
- end
101
-
102
- it 'must raise JWT::InvalidIatError when the iat value is not Numeric' do
103
- expect do
104
- Verify.verify_iat(payload.merge('iat' => 'not a number'), options)
105
- end.to raise_error JWT::InvalidIatError
106
- end
107
-
108
- it 'must raise JWT::InvalidIatError when the iat value is in the future' do
109
- expect do
110
- Verify.verify_iat(payload.merge('iat' => (iat + 120)), options)
111
- end.to raise_error JWT::InvalidIatError
112
- end
113
- end
114
-
115
- context '.verify_iss(payload, options)' do
116
- let(:iss) { 'ruby-jwt-gem' }
117
- let(:payload) { base_payload.merge('iss' => iss) }
118
-
119
- let(:invalid_token) { JWT.encode base_payload, payload[:secret] }
120
-
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
154
- end
155
- end
156
-
157
- context '.verify_jti(payload, options)' do
158
- let(:payload) { base_payload.merge('jti' => 'some-random-uuid-or-whatever') }
159
-
160
- it 'must allow any jti when the verfy_jti key in the options is truthy but not a proc' do
161
- Verify.verify_jti(payload, options.merge(verify_jti: true))
162
- end
163
-
164
- it 'must raise JWT::InvalidJtiError when the jti is missing' do
165
- expect do
166
- Verify.verify_jti(base_payload, options)
167
- end.to raise_error JWT::InvalidJtiError, /missing/i
168
- end
169
-
170
- it 'must raise JWT::InvalidJtiError when the jti is an empty string' do
171
- expect do
172
- Verify.verify_jti(base_payload.merge('jti' => ' '), options)
173
- end.to raise_error JWT::InvalidJtiError, /missing/i
174
- end
175
-
176
- it 'must raise JWT::InvalidJtiError when verify_jti proc returns false' do
177
- expect do
178
- Verify.verify_jti(payload, options.merge(verify_jti: ->(_jti) { false }))
179
- end.to raise_error JWT::InvalidJtiError, /invalid/i
180
- end
181
-
182
- it 'true proc should not raise JWT::InvalidJtiError' do
183
- Verify.verify_jti(payload, options.merge(verify_jti: ->(_jti) { true }))
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
198
- end
199
-
200
- context '.verify_not_before(payload, options)' do
201
- let(:payload) { base_payload.merge('nbf' => (Time.now.to_i + 5)) }
202
-
203
- it 'must raise JWT::ImmatureSignature when the nbf in the payload is in the future' do
204
- expect do
205
- Verify.verify_not_before(payload, options)
206
- end.to raise_error JWT::ImmatureSignature
207
- end
208
-
209
- it 'must allow some leeway in the token age when global leeway is configured' do
210
- Verify.verify_not_before(payload, options.merge(leeway: 10))
211
- end
212
-
213
- it 'must allow some leeway in the token age when nbf_leeway is configured' do
214
- Verify.verify_not_before(payload, options.merge(nbf_leeway: 10))
215
- end
216
- end
217
-
218
- context '.verify_sub(payload, options)' do
219
- let(:sub) { 'ruby jwt subject' }
220
-
221
- it 'must raise JWT::InvalidSubError when the subjects do not match' do
222
- expect do
223
- Verify.verify_sub(base_payload.merge('sub' => 'not-a-match'), options.merge(sub: sub))
224
- end.to raise_error JWT::InvalidSubError
225
- end
226
-
227
- it 'must allow a matching sub' do
228
- Verify.verify_sub(base_payload.merge('sub' => sub), options.merge(sub: sub))
229
- end
230
- end
231
- end
232
- end
data/spec/jwt_spec.rb DELETED
@@ -1,315 +0,0 @@
1
- require 'spec_helper'
2
- require 'jwt'
3
- require 'jwt/encode'
4
- require 'jwt/decode'
5
-
6
- describe JWT do
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
40
- end
41
-
42
- context 'alg: NONE' do
43
- let(:alg) { 'none' }
44
-
45
- it 'should generate a valid token' do
46
- token = JWT.encode payload, nil, alg
47
-
48
- expect(token).to eq data['NONE']
49
- end
50
-
51
- it 'should decode a valid token' do
52
- jwt_payload, header = JWT.decode data['NONE'], nil, false
53
-
54
- expect(header['alg']).to eq alg
55
- expect(jwt_payload).to eq payload
56
- end
57
-
58
- it 'should display a better error message if payload exp is_a?(Time)' do
59
- payload['exp'] = Time.now
60
-
61
- expect do
62
- JWT.encode payload, nil, alg
63
- end.to raise_error JWT::InvalidPayload
64
- end
65
-
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
68
-
69
- expect do
70
- JWT.encode payload, nil, alg
71
- end.to raise_error JWT::InvalidPayload
72
- end
73
- end
74
-
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
79
-
80
- expect(token).to eq data[alg]
81
- end
82
-
83
- it 'should decode a valid token' do
84
- jwt_payload, header = JWT.decode data[alg], data[:secret], true, algorithm: alg
85
-
86
- expect(header['alg']).to eq alg
87
- expect(jwt_payload).to eq payload
88
- end
89
-
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
95
-
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
101
- end
102
- end
103
-
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
108
-
109
- expect(token).to eq data[alg]
110
- end
111
-
112
- it 'should decode a valid token' do
113
- jwt_payload, header = JWT.decode data[alg], data[:rsa_public], true, algorithm: alg
114
-
115
- expect(header['alg']).to eq alg
116
- expect(jwt_payload).to eq payload
117
- end
118
-
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'))
121
-
122
- expect do
123
- JWT.decode data[alg], key, true, algorithm: alg
124
- end.to raise_error JWT::DecodeError
125
- end
126
-
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'))
129
-
130
- expect do
131
- JWT.decode data[alg], key, false
132
- end.not_to raise_error
133
- end
134
- end
135
- end
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
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
177
-
178
- let(:wrong_key) { OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec256-wrong-public.pem'))) }
179
-
180
- it 'should generate a valid token' do
181
- jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
182
-
183
- expect(header['alg']).to eq alg
184
- expect(jwt_payload).to eq payload
185
- end
186
-
187
- it 'should decode a valid token' do
188
- jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
189
-
190
- expect(header['alg']).to eq alg
191
- expect(jwt_payload).to eq payload
192
- end
193
-
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
199
-
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
206
- end
207
-
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
214
-
215
- it 'ECDSA curve_name should raise JWT::IncorrectAlgorithm' do
216
- key = OpenSSL::PKey::EC.new 'secp256k1'
217
- key.generate_key
218
-
219
- expect do
220
- JWT.encode payload, key, 'ES256'
221
- end.to raise_error JWT::IncorrectAlgorithm
222
-
223
- token = JWT.encode payload, data['ES256_private'], 'ES256'
224
- key.private_key = nil
225
-
226
- expect do
227
- JWT.decode token, key
228
- end.to raise_error JWT::IncorrectAlgorithm
229
- end
230
- end
231
-
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'
236
-
237
- expect do
238
- JWT.decode token, data[:secret], true, algorithm: 'HS384'
239
- end.to raise_error JWT::IncorrectAlgorithm
240
-
241
- expect do
242
- JWT.decode token, data[:secret], true, algorithm: 'HS256'
243
- end.not_to raise_error
244
- end
245
-
246
- it 'should raise JWT::IncorrectAlgorithm when algorithms array does not contain algorithm' do
247
- token = JWT.encode payload, data[:secret], 'HS512'
248
-
249
- expect do
250
- JWT.decode token, data[:secret], true, algorithms: ['HS384']
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
267
- end
268
- end
269
-
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]
277
- end
278
-
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
284
- end
285
- end
286
-
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
292
- end
293
-
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
302
- end
303
-
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
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
315
- end