jwt 2.1.0 → 2.2.3

Sign up to get free protection for your applications and to get access to all the features.
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
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
+
5
+ gem 'rubocop', '~> 0.52.0' # Same as codeclimate default
data/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # JWT
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/jwt.svg)](https://badge.fury.io/rb/jwt)
4
- [![Build Status](https://travis-ci.org/jwt/ruby-jwt.svg)](https://travis-ci.org/jwt/ruby-jwt)
4
+ [![Build Status](https://github.com/jwt/ruby-jwt/workflows/test/badge.svg?branch=master)](https://github.com/jwt/ruby-jwt/actions)
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
+ [![SourceLevel](https://app.sourcelevel.io/github/jwt/-/ruby-jwt.svg)](https://app.sourcelevel.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
 
@@ -15,11 +16,17 @@ If you have further questions related to development or usage, join us: [ruby-jw
15
16
  * Ruby 1.9.3 support was dropped at December 31st, 2016.
16
17
  * 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)
17
18
 
19
+ ## Sponsors
20
+
21
+ |Logo|Message|
22
+ |-|-|
23
+ |![auth0 logo](https://user-images.githubusercontent.com/83319/31722733-de95bbde-b3ea-11e7-96bf-4f4e8f915588.png)|If you want to quickly add secure token-based authentication to Ruby projects, feel free to check Auth0's Ruby SDK and free plan at [auth0.com/developers](https://auth0.com/developers?utm_source=GHsponsor&utm_medium=GHsponsor&utm_campaign=rubyjwt&utm_content=auth)|
24
+
18
25
  ## Installing
19
26
 
20
27
  ### Using Rubygems:
21
28
  ```bash
22
- sudo gem install jwt
29
+ gem install jwt
23
30
  ```
24
31
 
25
32
  ### Using Bundler:
@@ -31,7 +38,7 @@ And run `bundle install`
31
38
 
32
39
  ## Algorithms and Usage
33
40
 
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/).
41
+ 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
42
 
36
43
  See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1)
37
44
 
@@ -42,12 +49,12 @@ See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values
42
49
  ```ruby
43
50
  require 'jwt'
44
51
 
45
- payload = {:data => 'test'}
52
+ payload = { data: 'test' }
46
53
 
47
54
  # IMPORTANT: set nil as password parameter
48
55
  token = JWT.encode payload, nil, 'none'
49
56
 
50
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.
57
+ # eyJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.
51
58
  puts token
52
59
 
53
60
  # Set password to nil and validation to false otherwise this won't work
@@ -73,10 +80,25 @@ hmac_secret = 'my$ecretK3y'
73
80
 
74
81
  token = JWT.encode payload, hmac_secret, 'HS256'
75
82
 
76
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.ZxW8go9hz3ETCSfxFxpwSkYg_602gOPKearsf6DsxgY
83
+ # eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.pNIWIL34Jo13LViZAJACzK6Yf0qnvT_BuwOxiMCPE-Y
84
+ puts token
85
+
86
+ decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
87
+
88
+ # Array
89
+ # [
90
+ # {"data"=>"test"}, # payload
91
+ # {"alg"=>"HS256"} # header
92
+ # ]
93
+ puts decoded_token
94
+
95
+ # Without secret key
96
+ token = JWT.encode payload, nil, 'HS256'
97
+
98
+ # eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.pVzcY2dX8JNM3LzIYeP2B1e1Wcpt1K3TWVvIYSF4x-o
77
99
  puts token
78
100
 
79
- decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' }
101
+ decoded_token = JWT.decode token, nil, true, { algorithm: 'HS256' }
80
102
 
81
103
  # Array
82
104
  # [
@@ -104,10 +126,10 @@ rsa_public = rsa_private.public_key
104
126
 
105
127
  token = JWT.encode payload, rsa_private, 'RS256'
106
128
 
107
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ0ZXN0IjoiZGF0YSJ9.c2FynXNyi6_PeKxrDGxfS3OLwQ8lTDbWBWdq7oMviCy2ZfFpzvW2E_odCWJrbLof-eplHCsKzW7MGAntHMALXgclm_Cs9i2Exi6BZHzpr9suYkrhIjwqV1tCgMBCQpdeMwIq6SyKVjgH3L51ivIt0-GDDPDH1Rcut3jRQzp3Q35bg3tcI2iVg7t3Msvl9QrxXAdYNFiS5KXH22aJZ8X_O2HgqVYBXfSB1ygTYUmKTIIyLbntPQ7R22rFko1knGWOgQCoYXwbtpuKRZVFrxX958L2gUWgb4jEQNf3fhOtkBm1mJpj-7BGst00o8g_3P2zHy-3aKgpPo1XlKQGjRrrxA
129
+ # eyJhbGciOiJSUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.GplO4w1spRgvEJQ3-FOtZr-uC8L45Jt7SN0J4woBnEXG_OZBSNcZjAJWpjadVYEe2ev3oUBFDYM1N_-0BTVeFGGYvMewu8E6aMjSZvOpf1cZBew-Vt4poSq7goG2YRI_zNPt3af2lkPqXD796IKC5URrEvcgF5xFQ-6h07XRDpSRx1ECrNsUOt7UM3l1IB4doY11GzwQA5sHDTmUZ0-kBT76ZMf12Srg_N3hZwphxBtudYtN5VGZn420sVrQMdPE_7Ni3EiWT88j7WCr1xrF60l8sZT3yKCVleG7D2BEXacTntB7GktBv4Xo8OKnpwpqTpIlC05dMowMkz3rEAAYbQ
108
130
  puts token
109
131
 
110
- decoded_token = JWT.decode token, rsa_public, true, { :algorithm => 'RS256' }
132
+ decoded_token = JWT.decode token, rsa_public, true, { algorithm: 'RS256' }
111
133
 
112
134
  # Array
113
135
  # [
@@ -131,10 +153,10 @@ ecdsa_public.private_key = nil
131
153
 
132
154
  token = JWT.encode payload, ecdsa_key, 'ES256'
133
155
 
134
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJ0ZXN0IjoiZGF0YSJ9.MEQCIAtShrxRwP1L9SapqaT4f7hajDJH4t_rfm-YlZcNDsBNAiB64M4-JRfyS8nRMlywtQ9lHbvvec9U54KznzOe1YxTyA
156
+ # eyJhbGciOiJFUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.AlLW--kaF7EX1NMX9WJRuIW8NeRJbn2BLXHns7Q5TZr7Hy3lF6MOpMlp7GoxBFRLISQ6KrD0CJOrR8aogEsPeg
135
157
  puts token
136
158
 
137
- decoded_token = JWT.decode token, ecdsa_public, true, { :algorithm => 'ES256' }
159
+ decoded_token = JWT.decode token, ecdsa_public, true, { algorithm: 'ES256' }
138
160
 
139
161
  # Array
140
162
  # [
@@ -154,17 +176,17 @@ gem 'rbnacl'
154
176
 
155
177
  For more detailed installation instruction check the official [repository](https://github.com/cryptosphere/rbnacl) on GitHub.
156
178
 
157
- * ED25519
179
+ * ED25519
158
180
 
159
- ```ruby
160
- private_key = RbNaCl::Signatures::Ed25519::SigningKey.new("abcdefghijklmnopqrstuvwxyzABCDEF")
181
+ ```ruby
182
+ private_key = RbNaCl::Signatures::Ed25519::SigningKey.new('abcdefghijklmnopqrstuvwxyzABCDEF')
161
183
  public_key = private_key.verify_key
162
- token = JWT.encode payload, private_key, 'ED25519'
184
+ token = JWT.encode payload, private_key, 'ED25519'
163
185
 
164
- # eyJhbGciOiJFRDI1NTE5In0.eyJ0ZXN0IjoiZGF0YSJ9.-Ki0vxVOlsPXovPsYRT_9OXrLSgQd4RDAgCLY_PLmcP4q32RYy-yUUmX82ycegdekR9wo26me1wOzjmSU5nTCQ
186
+ # eyJhbGciOiJFRDI1NTE5In0.eyJkYXRhIjoidGVzdCJ9.6xIztXyOupskddGA_RvKU76V9b2dCQUYhoZEVFnRimJoPYIzZ2Fm47CWw8k2NTCNpgfAuxg9OXjaiVK7MvrbCQ
165
187
  puts token
166
188
 
167
- decoded_token = JWT.decode token, public_key, true, {:algorithm => 'ED25519' }
189
+ decoded_token = JWT.decode token, public_key, true, { algorithm: 'ED25519' }
168
190
  # Array
169
191
  # [
170
192
  # {"test"=>"data"}, # payload
@@ -175,7 +197,34 @@ decoded_token = JWT.decode token, public_key, true, {:algorithm => 'ED25519' }
175
197
 
176
198
  **RSASSA-PSS**
177
199
 
178
- Not implemented.
200
+ 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`.
201
+
202
+ ```ruby
203
+ gem 'openssl', '~> 2.1'
204
+ ```
205
+
206
+ * PS256 - RSASSA-PSS using SHA-256 hash algorithm
207
+ * PS384 - RSASSA-PSS using SHA-384 hash algorithm
208
+ * PS512 - RSASSA-PSS using SHA-512 hash algorithm
209
+
210
+ ```ruby
211
+ rsa_private = OpenSSL::PKey::RSA.generate 2048
212
+ rsa_public = rsa_private.public_key
213
+
214
+ token = JWT.encode payload, rsa_private, 'PS256'
215
+
216
+ # eyJhbGciOiJQUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.KEmqagMUHM-NcmXo6818ZazVTIAkn9qU9KQFT1c5Iq91n0KRpAI84jj4ZCdkysDlWokFs3Dmn4MhcXP03oJKLFgnoPL40_Wgg9iFr0jnIVvnMUp1kp2RFUbL0jqExGTRA3LdAhuvw6ZByGD1bkcWjDXygjQw-hxILrT1bENjdr0JhFd-cB0-ps5SB0mwhFNcUw-OM3Uu30B1-mlFaelUY8jHJYKwLTZPNxHzndt8RGXF8iZLp7dGb06HSCKMcVzhASGMH4ZdFystRe2hh31cwcvnl-Eo_D4cdwmpN3Abhk_8rkxawQJR3duh8HNKc4AyFPo7SabEaSu2gLnLfN3yfg
217
+ puts token
218
+
219
+ decoded_token = JWT.decode token, rsa_public, true, { algorithm: 'PS256' }
220
+
221
+ # Array
222
+ # [
223
+ # {"data"=>"test"}, # payload
224
+ # {"alg"=>"PS256"} # header
225
+ # ]
226
+ puts decoded_token
227
+ ```
179
228
 
180
229
  ## Support for reserved claim names
181
230
  JSON Web Token defines some reserved claim names and defines how they should be
@@ -190,7 +239,7 @@ used. JWT supports these reserved claim names:
190
239
  - 'sub' (Subject) Claim
191
240
 
192
241
  ## Add custom header fields
193
- Ruby-jwt gem supports custom [header fields] (https://tools.ietf.org/html/rfc7519#section-5)
242
+ Ruby-jwt gem supports custom [header fields](https://tools.ietf.org/html/rfc7519#section-5)
194
243
  To add custom header fields you need to pass `header_fields` parameter
195
244
 
196
245
  ```ruby
@@ -202,12 +251,12 @@ token = JWT.encode payload, key, algorithm='HS256', header_fields={}
202
251
  ```ruby
203
252
  require 'jwt'
204
253
 
205
- payload = {:data => 'test'}
254
+ payload = { data: 'test' }
206
255
 
207
256
  # IMPORTANT: set nil as password parameter
208
- token = JWT.encode payload, nil, 'none', { :typ => "JWT" }
257
+ token = JWT.encode payload, nil, 'none', { typ: 'JWT' }
209
258
 
210
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.
259
+ # eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJkYXRhIjoidGVzdCJ9.
211
260
  puts token
212
261
 
213
262
  # Set password to nil and validation to false otherwise this won't work
@@ -231,31 +280,37 @@ From [Oauth JSON Web Token 4.1.4. "exp" (Expiration Time) Claim](https://tools.i
231
280
 
232
281
  ```ruby
233
282
  exp = Time.now.to_i + 4 * 3600
234
- exp_payload = { :data => 'data', :exp => exp }
283
+ exp_payload = { data: 'data', exp: exp }
235
284
 
236
285
  token = JWT.encode exp_payload, hmac_secret, 'HS256'
237
286
 
238
287
  begin
239
- decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' }
288
+ decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
240
289
  rescue JWT::ExpiredSignature
241
290
  # Handle expired token, e.g. logout user or deny access
242
291
  end
243
292
  ```
244
293
 
294
+ The Expiration Claim verification can be disabled.
295
+ ```ruby
296
+ # Decode token without raising JWT::ExpiredSignature error
297
+ JWT.decode token, hmac_secret, true, { verify_expiration: false, algorithm: 'HS256' }
298
+ ```
299
+
245
300
  **Adding Leeway**
246
301
 
247
302
  ```ruby
248
303
  exp = Time.now.to_i - 10
249
304
  leeway = 30 # seconds
250
305
 
251
- exp_payload = { :data => 'data', :exp => exp }
306
+ exp_payload = { data: 'data', exp: exp }
252
307
 
253
308
  # build expired token
254
309
  token = JWT.encode exp_payload, hmac_secret, 'HS256'
255
310
 
256
311
  begin
257
312
  # add leeway to ensure the token is still accepted
258
- decoded_token = JWT.decode token, hmac_secret, true, { :exp_leeway => leeway, :algorithm => 'HS256' }
313
+ decoded_token = JWT.decode token, hmac_secret, true, { exp_leeway: leeway, algorithm: 'HS256' }
259
314
  rescue JWT::ExpiredSignature
260
315
  # Handle expired token, e.g. logout user or deny access
261
316
  end
@@ -271,31 +326,37 @@ From [Oauth JSON Web Token 4.1.5. "nbf" (Not Before) Claim](https://tools.ietf.o
271
326
 
272
327
  ```ruby
273
328
  nbf = Time.now.to_i - 3600
274
- nbf_payload = { :data => 'data', :nbf => nbf }
329
+ nbf_payload = { data: 'data', nbf: nbf }
275
330
 
276
331
  token = JWT.encode nbf_payload, hmac_secret, 'HS256'
277
332
 
278
333
  begin
279
- decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' }
334
+ decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
280
335
  rescue JWT::ImmatureSignature
281
336
  # Handle invalid token, e.g. logout user or deny access
282
337
  end
283
338
  ```
284
339
 
340
+ The Not Before Claim verification can be disabled.
341
+ ```ruby
342
+ # Decode token without raising JWT::ImmatureSignature error
343
+ JWT.decode token, hmac_secret, true, { verify_not_before: false, algorithm: 'HS256' }
344
+ ```
345
+
285
346
  **Adding Leeway**
286
347
 
287
348
  ```ruby
288
349
  nbf = Time.now.to_i + 10
289
350
  leeway = 30
290
351
 
291
- nbf_payload = { :data => 'data', :nbf => nbf }
352
+ nbf_payload = { data: 'data', nbf: nbf }
292
353
 
293
354
  # build expired token
294
355
  token = JWT.encode nbf_payload, hmac_secret, 'HS256'
295
356
 
296
357
  begin
297
358
  # add leeway to ensure the token is valid
298
- decoded_token = JWT.decode token, hmac_secret, true, { :nbf_leeway => leeway, :algorithm => 'HS256' }
359
+ decoded_token = JWT.decode token, hmac_secret, true, { nbf_leeway: leeway, algorithm: 'HS256' }
299
360
  rescue JWT::ImmatureSignature
300
361
  # Handle invalid token, e.g. logout user or deny access
301
362
  end
@@ -311,13 +372,13 @@ You can pass multiple allowed issuers as an Array, verification will pass if one
311
372
 
312
373
  ```ruby
313
374
  iss = 'My Awesome Company Inc. or https://my.awesome.website/'
314
- iss_payload = { :data => 'data', :iss => iss }
375
+ iss_payload = { data: 'data', iss: iss }
315
376
 
316
377
  token = JWT.encode iss_payload, hmac_secret, 'HS256'
317
378
 
318
379
  begin
319
380
  # Add iss to the validation to check if the token has been manipulated
320
- decoded_token = JWT.decode token, hmac_secret, true, { :iss => iss, :verify_iss => true, :algorithm => 'HS256' }
381
+ decoded_token = JWT.decode token, hmac_secret, true, { iss: iss, verify_iss: true, algorithm: 'HS256' }
321
382
  rescue JWT::InvalidIssuerError
322
383
  # Handle invalid token, e.g. logout user or deny access
323
384
  end
@@ -331,13 +392,13 @@ From [Oauth JSON Web Token 4.1.3. "aud" (Audience) Claim](https://tools.ietf.org
331
392
 
332
393
  ```ruby
333
394
  aud = ['Young', 'Old']
334
- aud_payload = { :data => 'data', :aud => aud }
395
+ aud_payload = { data: 'data', aud: aud }
335
396
 
336
397
  token = JWT.encode aud_payload, hmac_secret, 'HS256'
337
398
 
338
399
  begin
339
400
  # Add aud to the validation to check if the token has been manipulated
340
- decoded_token = JWT.decode token, hmac_secret, true, { :aud => aud, :verify_aud => true, :algorithm => 'HS256' }
401
+ decoded_token = JWT.decode token, hmac_secret, true, { aud: aud, verify_aud: true, algorithm: 'HS256' }
341
402
  rescue JWT::InvalidAudError
342
403
  # Handle invalid token, e.g. logout user or deny access
343
404
  puts 'Audience Error'
@@ -354,58 +415,40 @@ From [Oauth JSON Web Token 4.1.7. "jti" (JWT ID) Claim](https://tools.ietf.org/h
354
415
  # Use the secret and iat to create a unique key per request to prevent replay attacks
355
416
  jti_raw = [hmac_secret, iat].join(':').to_s
356
417
  jti = Digest::MD5.hexdigest(jti_raw)
357
- jti_payload = { :data => 'data', :iat => iat, :jti => jti }
418
+ jti_payload = { data: 'data', iat: iat, jti: jti }
358
419
 
359
420
  token = JWT.encode jti_payload, hmac_secret, 'HS256'
360
421
 
361
422
  begin
362
423
  # If :verify_jti is true, validation will pass if a JTI is present
363
- #decoded_token = JWT.decode token, hmac_secret, true, { :verify_jti => true, :algorithm => 'HS256' }
424
+ #decoded_token = JWT.decode token, hmac_secret, true, { verify_jti: true, algorithm: 'HS256' }
364
425
  # Alternatively, pass a proc with your own code to check if the JTI has already been used
365
- decoded_token = JWT.decode token, hmac_secret, true, { :verify_jti => proc { |jti| my_validation_method(jti) }, :algorithm => 'HS256' }
426
+ decoded_token = JWT.decode token, hmac_secret, true, { verify_jti: proc { |jti| my_validation_method(jti) }, algorithm: 'HS256' }
427
+ # or
428
+ decoded_token = JWT.decode token, hmac_secret, true, { verify_jti: proc { |jti, payload| my_validation_method(jti, payload) }, algorithm: 'HS256' }
366
429
  rescue JWT::InvalidJtiError
367
430
  # Handle invalid token, e.g. logout user or deny access
368
431
  puts 'Error'
369
432
  end
370
-
371
433
  ```
372
434
 
373
435
  ### Issued At Claim
374
436
 
375
437
  From [Oauth JSON Web Token 4.1.6. "iat" (Issued At) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.6):
376
438
 
377
- > 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.
439
+ > 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.
378
440
 
379
441
  **Handle Issued At Claim**
380
442
 
381
443
  ```ruby
382
444
  iat = Time.now.to_i
383
- iat_payload = { :data => 'data', :iat => iat }
445
+ iat_payload = { data: 'data', iat: iat }
384
446
 
385
447
  token = JWT.encode iat_payload, hmac_secret, 'HS256'
386
448
 
387
449
  begin
388
450
  # Add iat to the validation to check if the token has been manipulated
389
- decoded_token = JWT.decode token, hmac_secret, true, { :verify_iat => true, :algorithm => 'HS256' }
390
- rescue JWT::InvalidIatError
391
- # Handle invalid token, e.g. logout user or deny access
392
- end
393
- ```
394
-
395
- **Adding Leeway**
396
-
397
- ```ruby
398
- iat = Time.now.to_i + 10
399
- leeway = 30 # seconds
400
-
401
- iat_payload = { :data => 'data', :iat => iat }
402
-
403
- # build token issued in the future
404
- token = JWT.encode iat_payload, hmac_secret, 'HS256'
405
-
406
- begin
407
- # add leeway to ensure the token is accepted
408
- decoded_token = JWT.decode token, hmac_secret, true, { :iat_leeway => leeway, :verify_iat => true, :algorithm => 'HS256' }
451
+ decoded_token = JWT.decode token, hmac_secret, true, { verify_iat: true, algorithm: 'HS256' }
409
452
  rescue JWT::InvalidIatError
410
453
  # Handle invalid token, e.g. logout user or deny access
411
454
  end
@@ -419,18 +462,61 @@ From [Oauth JSON Web Token 4.1.2. "sub" (Subject) Claim](https://tools.ietf.org/
419
462
 
420
463
  ```ruby
421
464
  sub = 'Subject'
422
- sub_payload = { :data => 'data', :sub => sub }
465
+ sub_payload = { data: 'data', sub: sub }
423
466
 
424
467
  token = JWT.encode sub_payload, hmac_secret, 'HS256'
425
468
 
426
469
  begin
427
470
  # Add sub to the validation to check if the token has been manipulated
428
- decoded_token = JWT.decode token, hmac_secret, true, { 'sub' => sub, :verify_sub => true, :algorithm => 'HS256' }
471
+ decoded_token = JWT.decode token, hmac_secret, true, { sub: sub, verify_sub: true, algorithm: 'HS256' }
429
472
  rescue JWT::InvalidSubError
430
473
  # Handle invalid token, e.g. logout user or deny access
431
474
  end
432
475
  ```
433
476
 
477
+ ### JSON Web Key (JWK)
478
+
479
+ JWK is a JSON structure representing a cryptographic key. Currently only supports RSA public keys.
480
+
481
+ ```ruby
482
+ jwk = JWT::JWK.new(OpenSSL::PKey::RSA.new(2048))
483
+ payload, headers = { data: 'data' }, { kid: jwk.kid }
484
+
485
+ token = JWT.encode(payload, jwk.keypair, 'RS512', headers)
486
+
487
+ # The jwk loader would fetch the set of JWKs from a trusted source
488
+ jwk_loader = ->(options) do
489
+ @cached_keys = nil if options[:invalidate] # need to reload the keys
490
+ @cached_keys ||= { keys: [jwk.export] }
491
+ end
492
+
493
+ begin
494
+ JWT.decode(token, nil, true, { algorithms: ['RS512'], jwks: jwk_loader})
495
+ rescue JWT::JWKError
496
+ # Handle problems with the provided JWKs
497
+ rescue JWT::DecodeError
498
+ # Handle other decode related issues e.g. no kid in header, no matching public key found etc.
499
+ end
500
+ ```
501
+
502
+ or by passing JWK as a simple Hash
503
+
504
+ ```
505
+ jwks = { keys: [{ ... }] } # keys needs to be Symbol
506
+ JWT.decode(token, nil, true, { algorithms: ['RS512'], jwks: jwks})
507
+ ```
508
+
509
+ ### Importing and exporting JSON Web Keys
510
+
511
+ The ::JWT::JWK class can be used to import and export both the public key (default behaviour) and the private key. To include the private key in the export pass the `include_private` parameter to the export method.
512
+
513
+ ```ruby
514
+ jwk = JWT::JWK.new(OpenSSL::PKey::RSA.new(2048))
515
+
516
+ jwk_hash = jwk.export
517
+ jwk_hash_with_private_key = jwk.export(include_private: true)
518
+ ```
519
+
434
520
  # Development and Tests
435
521
 
436
522
  We depend on [Bundler](http://rubygems.org/gems/bundler) for defining gemspec and performing releases to rubygems.org, which can be done with
@@ -439,40 +525,19 @@ We depend on [Bundler](http://rubygems.org/gems/bundler) for defining gemspec an
439
525
  rake release
440
526
  ```
441
527
 
442
- The tests are written with rspec. Given you have installed the dependencies via bundler, you can run tests with
528
+ The tests are written with rspec. [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
443
529
 
444
530
  ```bash
445
- bundle exec rspec
531
+ bundle install
532
+ bundle exec appraisal rake test
446
533
  ```
447
534
 
448
535
  **If you want a release cut with your PR, please include a version bump according to [Semantic Versioning](http://semver.org/)**
449
536
 
450
537
  ## Contributors
451
538
 
452
- * Jordan Brough <github.jordanb@xoxy.net>
453
- * Ilya Zhitomirskiy <ilya@joindiaspora.com>
454
- * Daniel Grippi <daniel@joindiaspora.com>
455
- * Jeff Lindsay <progrium@gmail.com>
456
- * Bob Aman <bob@sporkmonger.com>
457
- * Micah Gates <github@mgates.com>
458
- * Rob Wygand <rob@wygand.com>
459
- * Ariel Salomon (Oscil8)
460
- * Paul Battley <pbattley@gmail.com>
461
- * Zane Shannon [@zshannon](https://github.com/zshannon)
462
- * Brian Fletcher [@punkle](https://github.com/punkle)
463
- * Alex [@ZhangHanDong](https://github.com/ZhangHanDong)
464
- * John Downey [@jtdowney](https://github.com/jtdowney)
465
- * Adam Greene [@skippy](https://github.com/skippy)
466
- * Tim Rudat [@excpt](https://github.com/excpt) <timrudat@gmail.com> - Maintainer
539
+ See `AUTHORS` file.
467
540
 
468
541
  ## License
469
542
 
470
- MIT
471
-
472
- Copyright (c) 2011 Jeff Lindsay
473
-
474
- 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:
475
-
476
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
477
-
478
- 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.
543
+ See `LICENSE` file.