jwt 2.0.0 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +5 -5
  2. data/.ebert.yml +2 -1
  3. data/.gitignore +1 -1
  4. data/.travis.yml +18 -3
  5. data/AUTHORS +84 -0
  6. data/Appraisals +18 -0
  7. data/CHANGELOG.md +223 -18
  8. data/README.md +136 -81
  9. data/lib/jwt.rb +9 -40
  10. data/lib/jwt/algos/ecdsa.rb +35 -0
  11. data/lib/jwt/algos/eddsa.rb +23 -0
  12. data/lib/jwt/algos/hmac.rb +34 -0
  13. data/lib/jwt/algos/ps.rb +43 -0
  14. data/lib/jwt/algos/rsa.rb +19 -0
  15. data/lib/jwt/algos/unsupported.rb +16 -0
  16. data/lib/jwt/base64.rb +19 -0
  17. data/lib/jwt/claims_validator.rb +33 -0
  18. data/lib/jwt/decode.rb +83 -25
  19. data/lib/jwt/default_options.rb +2 -1
  20. data/lib/jwt/encode.rb +42 -25
  21. data/lib/jwt/error.rb +4 -0
  22. data/lib/jwt/json.rb +18 -0
  23. data/lib/jwt/jwk.rb +31 -0
  24. data/lib/jwt/jwk/key_finder.rb +57 -0
  25. data/lib/jwt/jwk/rsa.rb +54 -0
  26. data/lib/jwt/security_utils.rb +6 -1
  27. data/lib/jwt/signature.rb +27 -79
  28. data/lib/jwt/verify.rb +5 -8
  29. data/lib/jwt/version.rb +2 -2
  30. data/ruby-jwt.gemspec +7 -4
  31. metadata +54 -63
  32. data/.reek.yml +0 -40
  33. data/Manifest +0 -8
  34. data/spec/fixtures/certs/ec256-private.pem +0 -8
  35. data/spec/fixtures/certs/ec256-public.pem +0 -4
  36. data/spec/fixtures/certs/ec256-wrong-private.pem +0 -8
  37. data/spec/fixtures/certs/ec256-wrong-public.pem +0 -4
  38. data/spec/fixtures/certs/ec384-private.pem +0 -9
  39. data/spec/fixtures/certs/ec384-public.pem +0 -5
  40. data/spec/fixtures/certs/ec384-wrong-private.pem +0 -9
  41. data/spec/fixtures/certs/ec384-wrong-public.pem +0 -5
  42. data/spec/fixtures/certs/ec512-private.pem +0 -10
  43. data/spec/fixtures/certs/ec512-public.pem +0 -6
  44. data/spec/fixtures/certs/ec512-wrong-private.pem +0 -10
  45. data/spec/fixtures/certs/ec512-wrong-public.pem +0 -6
  46. data/spec/fixtures/certs/rsa-1024-private.pem +0 -15
  47. data/spec/fixtures/certs/rsa-1024-public.pem +0 -6
  48. data/spec/fixtures/certs/rsa-2048-private.pem +0 -27
  49. data/spec/fixtures/certs/rsa-2048-public.pem +0 -9
  50. data/spec/fixtures/certs/rsa-2048-wrong-private.pem +0 -27
  51. data/spec/fixtures/certs/rsa-2048-wrong-public.pem +0 -9
  52. data/spec/fixtures/certs/rsa-4096-private.pem +0 -51
  53. data/spec/fixtures/certs/rsa-4096-public.pem +0 -14
  54. data/spec/integration/readme_examples_spec.rb +0 -202
  55. data/spec/jwt/verify_spec.rb +0 -219
  56. data/spec/jwt_spec.rb +0 -257
  57. data/spec/spec_helper.rb +0 -28
data/README.md CHANGED
@@ -5,8 +5,9 @@
5
5
  [![Code Climate](https://codeclimate.com/github/jwt/ruby-jwt/badges/gpa.svg)](https://codeclimate.com/github/jwt/ruby-jwt)
6
6
  [![Test Coverage](https://codeclimate.com/github/jwt/ruby-jwt/badges/coverage.svg)](https://codeclimate.com/github/jwt/ruby-jwt/coverage)
7
7
  [![Issue Count](https://codeclimate.com/github/jwt/ruby-jwt/badges/issue_count.svg)](https://codeclimate.com/github/jwt/ruby-jwt)
8
+ [![Ebert](https://ebertapp.io/github/jwt/ruby-jwt.svg)](https://ebertapp.io/github/jwt/ruby-jwt)
8
9
 
9
- A pure ruby implementation of the [RFC 7519 OAuth JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519) standard.
10
+ A ruby implementation of the [RFC 7519 OAuth JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519) standard.
10
11
 
11
12
  If you have further questions related to development or usage, join us: [ruby-jwt google group](https://groups.google.com/forum/#!forum/ruby-jwt).
12
13
 
@@ -31,7 +32,7 @@ And run `bundle install`
31
32
 
32
33
  ## Algorithms and Usage
33
34
 
34
- The JWT spec supports NONE, HMAC, RSASSA, ECDSA and RSASSA-PSS algorithms for cryptographic signing. Currently the jwt gem supports NONE, HMAC, RSASSA and ECDSA. If you are using cryptographic signing, you need to specify the algorithm in the options hash whenever you call JWT.decode to ensure that an attacker [cannot bypass the algorithm verification step](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/).
35
+ The JWT spec supports NONE, HMAC, RSASSA, ECDSA and RSASSA-PSS algorithms for cryptographic signing. Currently the jwt gem supports NONE, HMAC, RSASSA and ECDSA. If you are using cryptographic signing, you need to specify the algorithm in the options hash whenever you call JWT.decode to ensure that an attacker [cannot bypass the algorithm verification step](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). **It is strongly recommended that you hard code the algorithm, as you may leave yourself vulnerable by dynamically picking the algorithm**
35
36
 
36
37
  See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1)
37
38
 
@@ -42,12 +43,12 @@ See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values
42
43
  ```ruby
43
44
  require 'jwt'
44
45
 
45
- payload = {:data => 'test'}
46
+ payload = { data: 'test' }
46
47
 
47
48
  # IMPORTANT: set nil as password parameter
48
49
  token = JWT.encode payload, nil, 'none'
49
50
 
50
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.
51
+ # eyJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.
51
52
  puts token
52
53
 
53
54
  # Set password to nil and validation to false otherwise this won't work
@@ -61,9 +62,9 @@ decoded_token = JWT.decode token, nil, false
61
62
  puts decoded_token
62
63
  ```
63
64
 
64
- **HMAC** (default: HS256)
65
+ **HMAC**
65
66
 
66
- * HS256 - HMAC using SHA-256 hash algorithm (default)
67
+ * HS256 - HMAC using SHA-256 hash algorithm
67
68
  * HS512256 - HMAC using SHA-512-256 hash algorithm (only available with RbNaCl; see note below)
68
69
  * HS384 - HMAC using SHA-384 hash algorithm
69
70
  * HS512 - HMAC using SHA-512 hash algorithm
@@ -73,10 +74,25 @@ hmac_secret = 'my$ecretK3y'
73
74
 
74
75
  token = JWT.encode payload, hmac_secret, 'HS256'
75
76
 
76
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.ZxW8go9hz3ETCSfxFxpwSkYg_602gOPKearsf6DsxgY
77
+ # eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.pNIWIL34Jo13LViZAJACzK6Yf0qnvT_BuwOxiMCPE-Y
77
78
  puts token
78
79
 
79
- decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' }
80
+ decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
81
+
82
+ # Array
83
+ # [
84
+ # {"data"=>"test"}, # payload
85
+ # {"alg"=>"HS256"} # header
86
+ # ]
87
+ puts decoded_token
88
+
89
+ # Without secret key
90
+ token = JWT.encode payload, nil, 'HS256'
91
+
92
+ # eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.pVzcY2dX8JNM3LzIYeP2B1e1Wcpt1K3TWVvIYSF4x-o
93
+ puts token
94
+
95
+ decoded_token = JWT.decode token, nil, true, { algorithm: 'HS256' }
80
96
 
81
97
  # Array
82
98
  # [
@@ -104,10 +120,10 @@ rsa_public = rsa_private.public_key
104
120
 
105
121
  token = JWT.encode payload, rsa_private, 'RS256'
106
122
 
107
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ0ZXN0IjoiZGF0YSJ9.c2FynXNyi6_PeKxrDGxfS3OLwQ8lTDbWBWdq7oMviCy2ZfFpzvW2E_odCWJrbLof-eplHCsKzW7MGAntHMALXgclm_Cs9i2Exi6BZHzpr9suYkrhIjwqV1tCgMBCQpdeMwIq6SyKVjgH3L51ivIt0-GDDPDH1Rcut3jRQzp3Q35bg3tcI2iVg7t3Msvl9QrxXAdYNFiS5KXH22aJZ8X_O2HgqVYBXfSB1ygTYUmKTIIyLbntPQ7R22rFko1knGWOgQCoYXwbtpuKRZVFrxX958L2gUWgb4jEQNf3fhOtkBm1mJpj-7BGst00o8g_3P2zHy-3aKgpPo1XlKQGjRrrxA
123
+ # eyJhbGciOiJSUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.GplO4w1spRgvEJQ3-FOtZr-uC8L45Jt7SN0J4woBnEXG_OZBSNcZjAJWpjadVYEe2ev3oUBFDYM1N_-0BTVeFGGYvMewu8E6aMjSZvOpf1cZBew-Vt4poSq7goG2YRI_zNPt3af2lkPqXD796IKC5URrEvcgF5xFQ-6h07XRDpSRx1ECrNsUOt7UM3l1IB4doY11GzwQA5sHDTmUZ0-kBT76ZMf12Srg_N3hZwphxBtudYtN5VGZn420sVrQMdPE_7Ni3EiWT88j7WCr1xrF60l8sZT3yKCVleG7D2BEXacTntB7GktBv4Xo8OKnpwpqTpIlC05dMowMkz3rEAAYbQ
108
124
  puts token
109
125
 
110
- decoded_token = JWT.decode token, rsa_public, true, { :algorithm => 'RS256' }
126
+ decoded_token = JWT.decode token, rsa_public, true, { algorithm: 'RS256' }
111
127
 
112
128
  # Array
113
129
  # [
@@ -131,10 +147,10 @@ ecdsa_public.private_key = nil
131
147
 
132
148
  token = JWT.encode payload, ecdsa_key, 'ES256'
133
149
 
134
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJ0ZXN0IjoiZGF0YSJ9.MEQCIAtShrxRwP1L9SapqaT4f7hajDJH4t_rfm-YlZcNDsBNAiB64M4-JRfyS8nRMlywtQ9lHbvvec9U54KznzOe1YxTyA
150
+ # eyJhbGciOiJFUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.AlLW--kaF7EX1NMX9WJRuIW8NeRJbn2BLXHns7Q5TZr7Hy3lF6MOpMlp7GoxBFRLISQ6KrD0CJOrR8aogEsPeg
135
151
  puts token
136
152
 
137
- decoded_token = JWT.decode token, ecdsa_public, true, { :algorithm => 'ES256' }
153
+ decoded_token = JWT.decode token, ecdsa_public, true, { algorithm: 'ES256' }
138
154
 
139
155
  # Array
140
156
  # [
@@ -144,9 +160,65 @@ decoded_token = JWT.decode token, ecdsa_public, true, { :algorithm => 'ES256' }
144
160
  puts decoded_token
145
161
  ```
146
162
 
163
+ **EDDSA**
164
+
165
+ In order to use this algorithm you need to add the `RbNaCl` gem to you `Gemfile`.
166
+
167
+ ```ruby
168
+ gem 'rbnacl'
169
+ ```
170
+
171
+ For more detailed installation instruction check the official [repository](https://github.com/cryptosphere/rbnacl) on GitHub.
172
+
173
+ * ED25519
174
+
175
+ ```ruby
176
+ private_key = RbNaCl::Signatures::Ed25519::SigningKey.new('abcdefghijklmnopqrstuvwxyzABCDEF')
177
+ public_key = private_key.verify_key
178
+ token = JWT.encode payload, private_key, 'ED25519'
179
+
180
+ # eyJhbGciOiJFRDI1NTE5In0.eyJkYXRhIjoidGVzdCJ9.6xIztXyOupskddGA_RvKU76V9b2dCQUYhoZEVFnRimJoPYIzZ2Fm47CWw8k2NTCNpgfAuxg9OXjaiVK7MvrbCQ
181
+ puts token
182
+
183
+ decoded_token = JWT.decode token, public_key, true, { algorithm: 'ED25519' }
184
+ # Array
185
+ # [
186
+ # {"test"=>"data"}, # payload
187
+ # {"alg"=>"ED25519"} # header
188
+ # ]
189
+
190
+ ```
191
+
147
192
  **RSASSA-PSS**
148
193
 
149
- Not implemented.
194
+ In order to use this algorithm you need to add the `openssl` gem to you `Gemfile` with a version greater or equal to `2.1`.
195
+
196
+ ```ruby
197
+ gem 'openssl', '~> 2.1'
198
+ ```
199
+
200
+ * PS256 - RSASSA-PSS using SHA-256 hash algorithm
201
+ * PS384 - RSASSA-PSS using SHA-384 hash algorithm
202
+ * PS512 - RSASSA-PSS using SHA-512 hash algorithm
203
+
204
+ ```ruby
205
+ rsa_private = OpenSSL::PKey::RSA.generate 2048
206
+ rsa_public = rsa_private.public_key
207
+
208
+ token = JWT.encode payload, rsa_private, 'PS256'
209
+
210
+ # eyJhbGciOiJQUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.KEmqagMUHM-NcmXo6818ZazVTIAkn9qU9KQFT1c5Iq91n0KRpAI84jj4ZCdkysDlWokFs3Dmn4MhcXP03oJKLFgnoPL40_Wgg9iFr0jnIVvnMUp1kp2RFUbL0jqExGTRA3LdAhuvw6ZByGD1bkcWjDXygjQw-hxILrT1bENjdr0JhFd-cB0-ps5SB0mwhFNcUw-OM3Uu30B1-mlFaelUY8jHJYKwLTZPNxHzndt8RGXF8iZLp7dGb06HSCKMcVzhASGMH4ZdFystRe2hh31cwcvnl-Eo_D4cdwmpN3Abhk_8rkxawQJR3duh8HNKc4AyFPo7SabEaSu2gLnLfN3yfg
211
+ puts token
212
+
213
+ decoded_token = JWT.decode token, rsa_public, true, { algorithm: 'PS256' }
214
+
215
+ # Array
216
+ # [
217
+ # {"data"=>"test"}, # payload
218
+ # {"alg"=>"PS256"} # header
219
+ # ]
220
+ puts decoded_token
221
+ ```
150
222
 
151
223
  ## Support for reserved claim names
152
224
  JSON Web Token defines some reserved claim names and defines how they should be
@@ -161,7 +233,7 @@ used. JWT supports these reserved claim names:
161
233
  - 'sub' (Subject) Claim
162
234
 
163
235
  ## Add custom header fields
164
- Ruby-jwt gem supports custom [header fields] (https://tools.ietf.org/html/rfc7519#section-5)
236
+ Ruby-jwt gem supports custom [header fields](https://tools.ietf.org/html/rfc7519#section-5)
165
237
  To add custom header fields you need to pass `header_fields` parameter
166
238
 
167
239
  ```ruby
@@ -173,12 +245,12 @@ token = JWT.encode payload, key, algorithm='HS256', header_fields={}
173
245
  ```ruby
174
246
  require 'jwt'
175
247
 
176
- payload = {:data => 'test'}
248
+ payload = { data: 'test' }
177
249
 
178
250
  # IMPORTANT: set nil as password parameter
179
- token = JWT.encode payload, nil, 'none', { :typ => "JWT" }
251
+ token = JWT.encode payload, nil, 'none', { typ: 'JWT' }
180
252
 
181
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.
253
+ # eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJkYXRhIjoidGVzdCJ9.
182
254
  puts token
183
255
 
184
256
  # Set password to nil and validation to false otherwise this won't work
@@ -202,12 +274,12 @@ From [Oauth JSON Web Token 4.1.4. "exp" (Expiration Time) Claim](https://tools.i
202
274
 
203
275
  ```ruby
204
276
  exp = Time.now.to_i + 4 * 3600
205
- exp_payload = { :data => 'data', :exp => exp }
277
+ exp_payload = { data: 'data', exp: exp }
206
278
 
207
279
  token = JWT.encode exp_payload, hmac_secret, 'HS256'
208
280
 
209
281
  begin
210
- decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' }
282
+ decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
211
283
  rescue JWT::ExpiredSignature
212
284
  # Handle expired token, e.g. logout user or deny access
213
285
  end
@@ -219,14 +291,14 @@ end
219
291
  exp = Time.now.to_i - 10
220
292
  leeway = 30 # seconds
221
293
 
222
- exp_payload = { :data => 'data', :exp => exp }
294
+ exp_payload = { data: 'data', exp: exp }
223
295
 
224
296
  # build expired token
225
297
  token = JWT.encode exp_payload, hmac_secret, 'HS256'
226
298
 
227
299
  begin
228
300
  # add leeway to ensure the token is still accepted
229
- decoded_token = JWT.decode token, hmac_secret, true, { :exp_leeway => leeway, :algorithm => 'HS256' }
301
+ decoded_token = JWT.decode token, hmac_secret, true, { exp_leeway: leeway, algorithm: 'HS256' }
230
302
  rescue JWT::ExpiredSignature
231
303
  # Handle expired token, e.g. logout user or deny access
232
304
  end
@@ -242,12 +314,12 @@ From [Oauth JSON Web Token 4.1.5. "nbf" (Not Before) Claim](https://tools.ietf.o
242
314
 
243
315
  ```ruby
244
316
  nbf = Time.now.to_i - 3600
245
- nbf_payload = { :data => 'data', :nbf => nbf }
317
+ nbf_payload = { data: 'data', nbf: nbf }
246
318
 
247
319
  token = JWT.encode nbf_payload, hmac_secret, 'HS256'
248
320
 
249
321
  begin
250
- decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' }
322
+ decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
251
323
  rescue JWT::ImmatureSignature
252
324
  # Handle invalid token, e.g. logout user or deny access
253
325
  end
@@ -259,14 +331,14 @@ end
259
331
  nbf = Time.now.to_i + 10
260
332
  leeway = 30
261
333
 
262
- nbf_payload = { :data => 'data', :nbf => nbf }
334
+ nbf_payload = { data: 'data', nbf: nbf }
263
335
 
264
336
  # build expired token
265
337
  token = JWT.encode nbf_payload, hmac_secret, 'HS256'
266
338
 
267
339
  begin
268
340
  # add leeway to ensure the token is valid
269
- decoded_token = JWT.decode token, hmac_secret, true, { :nbf_leeway => leeway, :algorithm => 'HS256' }
341
+ decoded_token = JWT.decode token, hmac_secret, true, { nbf_leeway: leeway, algorithm: 'HS256' }
270
342
  rescue JWT::ImmatureSignature
271
343
  # Handle invalid token, e.g. logout user or deny access
272
344
  end
@@ -282,13 +354,13 @@ You can pass multiple allowed issuers as an Array, verification will pass if one
282
354
 
283
355
  ```ruby
284
356
  iss = 'My Awesome Company Inc. or https://my.awesome.website/'
285
- iss_payload = { :data => 'data', :iss => iss }
357
+ iss_payload = { data: 'data', iss: iss }
286
358
 
287
359
  token = JWT.encode iss_payload, hmac_secret, 'HS256'
288
360
 
289
361
  begin
290
362
  # Add iss to the validation to check if the token has been manipulated
291
- decoded_token = JWT.decode token, hmac_secret, true, { :iss => iss, :verify_iss => true, :algorithm => 'HS256' }
363
+ decoded_token = JWT.decode token, hmac_secret, true, { iss: iss, verify_iss: true, algorithm: 'HS256' }
292
364
  rescue JWT::InvalidIssuerError
293
365
  # Handle invalid token, e.g. logout user or deny access
294
366
  end
@@ -302,13 +374,13 @@ From [Oauth JSON Web Token 4.1.3. "aud" (Audience) Claim](https://tools.ietf.org
302
374
 
303
375
  ```ruby
304
376
  aud = ['Young', 'Old']
305
- aud_payload = { :data => 'data', :aud => aud }
377
+ aud_payload = { data: 'data', aud: aud }
306
378
 
307
379
  token = JWT.encode aud_payload, hmac_secret, 'HS256'
308
380
 
309
381
  begin
310
382
  # Add aud to the validation to check if the token has been manipulated
311
- decoded_token = JWT.decode token, hmac_secret, true, { :aud => aud, :verify_aud => true, :algorithm => 'HS256' }
383
+ decoded_token = JWT.decode token, hmac_secret, true, { aud: aud, verify_aud: true, algorithm: 'HS256' }
312
384
  rescue JWT::InvalidAudError
313
385
  # Handle invalid token, e.g. logout user or deny access
314
386
  puts 'Audience Error'
@@ -325,58 +397,38 @@ From [Oauth JSON Web Token 4.1.7. "jti" (JWT ID) Claim](https://tools.ietf.org/h
325
397
  # Use the secret and iat to create a unique key per request to prevent replay attacks
326
398
  jti_raw = [hmac_secret, iat].join(':').to_s
327
399
  jti = Digest::MD5.hexdigest(jti_raw)
328
- jti_payload = { :data => 'data', :iat => iat, :jti => jti }
400
+ jti_payload = { data: 'data', iat: iat, jti: jti }
329
401
 
330
402
  token = JWT.encode jti_payload, hmac_secret, 'HS256'
331
403
 
332
404
  begin
333
405
  # If :verify_jti is true, validation will pass if a JTI is present
334
- #decoded_token = JWT.decode token, hmac_secret, true, { :verify_jti => true, :algorithm => 'HS256' }
406
+ #decoded_token = JWT.decode token, hmac_secret, true, { verify_jti: true, algorithm: 'HS256' }
335
407
  # Alternatively, pass a proc with your own code to check if the JTI has already been used
336
- decoded_token = JWT.decode token, hmac_secret, true, { :verify_jti => proc { |jti| my_validation_method(jti) }, :algorithm => 'HS256' }
408
+ decoded_token = JWT.decode token, hmac_secret, true, { verify_jti: proc { |jti| my_validation_method(jti) }, algorithm: 'HS256' }
337
409
  rescue JWT::InvalidJtiError
338
410
  # Handle invalid token, e.g. logout user or deny access
339
411
  puts 'Error'
340
412
  end
341
-
342
413
  ```
343
414
 
344
415
  ### Issued At Claim
345
416
 
346
417
  From [Oauth JSON Web Token 4.1.6. "iat" (Issued At) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.6):
347
418
 
348
- > The `iat` (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value MUST be a number containing a ***NumericDate*** value. Use of this claim is OPTIONAL.
419
+ > The `iat` (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. The `leeway` option is not taken into account when verifying this claim. The `iat_leeway` option was removed in version 2.2.0. Its value MUST be a number containing a ***NumericDate*** value. Use of this claim is OPTIONAL.
349
420
 
350
421
  **Handle Issued At Claim**
351
422
 
352
423
  ```ruby
353
424
  iat = Time.now.to_i
354
- iat_payload = { :data => 'data', :iat => iat }
425
+ iat_payload = { data: 'data', iat: iat }
355
426
 
356
427
  token = JWT.encode iat_payload, hmac_secret, 'HS256'
357
428
 
358
429
  begin
359
430
  # Add iat to the validation to check if the token has been manipulated
360
- decoded_token = JWT.decode token, hmac_secret, true, { :verify_iat => true, :algorithm => 'HS256' }
361
- rescue JWT::InvalidIatError
362
- # Handle invalid token, e.g. logout user or deny access
363
- end
364
- ```
365
-
366
- **Adding Leeway**
367
-
368
- ```ruby
369
- iat = Time.now.to_i + 10
370
- leeway = 30 # seconds
371
-
372
- iat_payload = { :data => 'data', :iat => iat }
373
-
374
- # build token issued in the future
375
- token = JWT.encode iat_payload, hmac_secret, 'HS256'
376
-
377
- begin
378
- # add leeway to ensure the token is accepted
379
- decoded_token = JWT.decode token, hmac_secret, true, { :iat_leeway => leeway, :verify_iat => true, :algorithm => 'HS256' }
431
+ decoded_token = JWT.decode token, hmac_secret, true, { verify_iat: true, algorithm: 'HS256' }
380
432
  rescue JWT::InvalidIatError
381
433
  # Handle invalid token, e.g. logout user or deny access
382
434
  end
@@ -390,18 +442,43 @@ From [Oauth JSON Web Token 4.1.2. "sub" (Subject) Claim](https://tools.ietf.org/
390
442
 
391
443
  ```ruby
392
444
  sub = 'Subject'
393
- sub_payload = { :data => 'data', :sub => sub }
445
+ sub_payload = { data: 'data', sub: sub }
394
446
 
395
447
  token = JWT.encode sub_payload, hmac_secret, 'HS256'
396
448
 
397
449
  begin
398
450
  # Add sub to the validation to check if the token has been manipulated
399
- decoded_token = JWT.decode token, hmac_secret, true, { 'sub' => sub, :verify_sub => true, :algorithm => 'HS256' }
451
+ decoded_token = JWT.decode token, hmac_secret, true, { sub: sub, verify_sub: true, algorithm: 'HS256' }
400
452
  rescue JWT::InvalidSubError
401
453
  # Handle invalid token, e.g. logout user or deny access
402
454
  end
403
455
  ```
404
456
 
457
+ ### JSON Web Key (JWK)
458
+
459
+ JWK is a JSON structure representing a cryptographic key. Currently only supports RSA public keys.
460
+
461
+ ```ruby
462
+ jwk = JWT::JWK.new(OpenSSL::PKey::RSA.new(2048))
463
+ payload, headers = { data: 'data' }, { kid: jwk.kid }
464
+
465
+ token = JWT.encode(payload, jwk.keypair, 'RS512', headers)
466
+
467
+ # The jwk loader would fetch the set of JWKs from a trusted source
468
+ jwk_loader = ->(options) do
469
+ @cached_keys = nil if options[:invalidate] # need to reload the keys
470
+ @cached_keys ||= { keys: [jwk.export] }
471
+ end
472
+
473
+ begin
474
+ JWT.decode(token, nil, true, { algorithms: ['RS512'], jwks: jwk_loader})
475
+ rescue JWT::JWKError
476
+ # Handle problems with the provided JWKs
477
+ rescue JWT::DecodeError
478
+ # Handle other decode related issues e.g. no kid in header, no matching public key found etc.
479
+ end
480
+ ```
481
+
405
482
  # Development and Tests
406
483
 
407
484
  We depend on [Bundler](http://rubygems.org/gems/bundler) for defining gemspec and performing releases to rubygems.org, which can be done with
@@ -420,30 +497,8 @@ bundle exec rspec
420
497
 
421
498
  ## Contributors
422
499
 
423
- * Jordan Brough <github.jordanb@xoxy.net>
424
- * Ilya Zhitomirskiy <ilya@joindiaspora.com>
425
- * Daniel Grippi <daniel@joindiaspora.com>
426
- * Jeff Lindsay <progrium@gmail.com>
427
- * Bob Aman <bob@sporkmonger.com>
428
- * Micah Gates <github@mgates.com>
429
- * Rob Wygand <rob@wygand.com>
430
- * Ariel Salomon (Oscil8)
431
- * Paul Battley <pbattley@gmail.com>
432
- * Zane Shannon [@zshannon](https://github.com/zshannon)
433
- * Brian Fletcher [@punkle](https://github.com/punkle)
434
- * Alex [@ZhangHanDong](https://github.com/ZhangHanDong)
435
- * John Downey [@jtdowney](https://github.com/jtdowney)
436
- * Adam Greene [@skippy](https://github.com/skippy)
437
- * Tim Rudat [@excpt](https://github.com/excpt) <timrudat@gmail.com> - Maintainer
500
+ See `AUTHORS` file.
438
501
 
439
502
  ## License
440
503
 
441
- MIT
442
-
443
- Copyright (c) 2011 Jeff Lindsay
444
-
445
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
446
-
447
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
448
-
449
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
504
+ See `LICENSE` file.
data/lib/jwt.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'base64'
3
+ require 'jwt/base64'
4
+ require 'jwt/json'
4
5
  require 'jwt/decode'
5
6
  require 'jwt/default_options'
6
7
  require 'jwt/encode'
7
8
  require 'jwt/error'
8
- require 'jwt/signature'
9
- require 'jwt/verify'
9
+ require 'jwt/jwk'
10
10
 
11
11
  # JSON Web Token implementation
12
12
  #
@@ -18,44 +18,13 @@ module JWT
18
18
  module_function
19
19
 
20
20
  def encode(payload, key, algorithm = 'HS256', header_fields = {})
21
- encoder = Encode.new payload, key, algorithm, header_fields
22
- encoder.segments
21
+ Encode.new(payload: payload,
22
+ key: key,
23
+ algorithm: algorithm,
24
+ headers: header_fields).segments
23
25
  end
24
26
 
25
- def decode(jwt, key = nil, verify = true, custom_options = {}, &keyfinder)
26
- raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
27
-
28
- merged_options = DEFAULT_OPTIONS.merge(custom_options)
29
-
30
- decoder = Decode.new jwt, verify
31
- header, payload, signature, signing_input = decoder.decode_segments
32
- decode_verify_signature(key, header, payload, signature, signing_input, merged_options, &keyfinder) if verify
33
-
34
- Verify.verify_claims(payload, merged_options) if verify
35
-
36
- raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
37
-
38
- [payload, header]
39
- end
40
-
41
- def decode_verify_signature(key, header, payload, signature, signing_input, options, &keyfinder)
42
- algo, key = signature_algorithm_and_key(header, payload, key, &keyfinder)
43
-
44
- raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') unless options[:algorithm]
45
- raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless algo == options[:algorithm]
46
-
47
- Signature.verify(algo, key, signing_input, signature)
48
- end
49
-
50
- def signature_algorithm_and_key(header, payload, key, &keyfinder)
51
- if keyfinder
52
- key = if keyfinder.arity == 2
53
- yield(header, payload)
54
- else
55
- yield(header)
56
- end
57
- raise JWT::DecodeError, 'No verification key available' unless key
58
- end
59
- [header['alg'], key]
27
+ def decode(jwt, key = nil, verify = true, options = {}, &keyfinder)
28
+ Decode.new(jwt, key, verify, DEFAULT_OPTIONS.merge(options), &keyfinder).decode_segments
60
29
  end
61
30
  end