jwt 1.5.6 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +5 -5
  2. data/.ebert.yml +18 -0
  3. data/.gitignore +1 -1
  4. data/.rubocop.yml +96 -0
  5. data/.travis.yml +26 -10
  6. data/AUTHORS +84 -0
  7. data/Appraisals +18 -0
  8. data/CHANGELOG.md +296 -10
  9. data/Gemfile +0 -1
  10. data/README.md +182 -64
  11. data/lib/jwt.rb +14 -176
  12. data/lib/jwt/algos/ecdsa.rb +35 -0
  13. data/lib/jwt/algos/eddsa.rb +23 -0
  14. data/lib/jwt/algos/hmac.rb +34 -0
  15. data/lib/jwt/algos/ps.rb +43 -0
  16. data/lib/jwt/algos/rsa.rb +19 -0
  17. data/lib/jwt/algos/unsupported.rb +16 -0
  18. data/lib/jwt/base64.rb +19 -0
  19. data/lib/jwt/claims_validator.rb +33 -0
  20. data/lib/jwt/decode.rb +81 -31
  21. data/lib/jwt/default_options.rb +15 -0
  22. data/lib/jwt/encode.rb +68 -0
  23. data/lib/jwt/error.rb +6 -0
  24. data/lib/jwt/json.rb +10 -9
  25. data/lib/jwt/jwk.rb +31 -0
  26. data/lib/jwt/jwk/key_finder.rb +57 -0
  27. data/lib/jwt/jwk/rsa.rb +54 -0
  28. data/lib/jwt/security_utils.rb +57 -0
  29. data/lib/jwt/signature.rb +54 -0
  30. data/lib/jwt/verify.rb +45 -53
  31. data/lib/jwt/version.rb +3 -3
  32. data/ruby-jwt.gemspec +11 -7
  33. metadata +76 -67
  34. data/Manifest +0 -8
  35. data/spec/fixtures/certs/ec256-private.pem +0 -8
  36. data/spec/fixtures/certs/ec256-public.pem +0 -4
  37. data/spec/fixtures/certs/ec256-wrong-private.pem +0 -8
  38. data/spec/fixtures/certs/ec256-wrong-public.pem +0 -4
  39. data/spec/fixtures/certs/ec384-private.pem +0 -9
  40. data/spec/fixtures/certs/ec384-public.pem +0 -5
  41. data/spec/fixtures/certs/ec384-wrong-private.pem +0 -9
  42. data/spec/fixtures/certs/ec384-wrong-public.pem +0 -5
  43. data/spec/fixtures/certs/ec512-private.pem +0 -10
  44. data/spec/fixtures/certs/ec512-public.pem +0 -6
  45. data/spec/fixtures/certs/ec512-wrong-private.pem +0 -10
  46. data/spec/fixtures/certs/ec512-wrong-public.pem +0 -6
  47. data/spec/fixtures/certs/rsa-1024-private.pem +0 -15
  48. data/spec/fixtures/certs/rsa-1024-public.pem +0 -6
  49. data/spec/fixtures/certs/rsa-2048-private.pem +0 -27
  50. data/spec/fixtures/certs/rsa-2048-public.pem +0 -9
  51. data/spec/fixtures/certs/rsa-2048-wrong-private.pem +0 -27
  52. data/spec/fixtures/certs/rsa-2048-wrong-public.pem +0 -9
  53. data/spec/fixtures/certs/rsa-4096-private.pem +0 -51
  54. data/spec/fixtures/certs/rsa-4096-public.pem +0 -14
  55. data/spec/integration/readme_examples_spec.rb +0 -190
  56. data/spec/jwt/verify_spec.rb +0 -197
  57. data/spec/jwt_spec.rb +0 -240
  58. data/spec/spec_helper.rb +0 -31
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  source 'https://rubygems.org'
3
2
 
4
3
  gemspec
data/README.md CHANGED
@@ -1,17 +1,19 @@
1
1
  # JWT
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/jwt.svg)](https://badge.fury.io/rb/jwt)
3
4
  [![Build Status](https://travis-ci.org/jwt/ruby-jwt.svg)](https://travis-ci.org/jwt/ruby-jwt)
4
5
  [![Code Climate](https://codeclimate.com/github/jwt/ruby-jwt/badges/gpa.svg)](https://codeclimate.com/github/jwt/ruby-jwt)
5
6
  [![Test Coverage](https://codeclimate.com/github/jwt/ruby-jwt/badges/coverage.svg)](https://codeclimate.com/github/jwt/ruby-jwt/coverage)
6
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)
7
9
 
8
- 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.
9
11
 
10
- If you have further questions releated to development or usage, join us: [ruby-jwt google group](https://groups.google.com/forum/#!forum/ruby-jwt).
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).
11
13
 
12
14
  ## Announcements
13
15
 
14
- * Ruby 1.9.3 support will be dropped by December 31st, 2016.
16
+ * Ruby 1.9.3 support was dropped at December 31st, 2016.
15
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)
16
18
 
17
19
  ## Installing
@@ -30,7 +32,7 @@ And run `bundle install`
30
32
 
31
33
  ## Algorithms and Usage
32
34
 
33
- 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**
34
36
 
35
37
  See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1)
36
38
 
@@ -41,12 +43,12 @@ See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values
41
43
  ```ruby
42
44
  require 'jwt'
43
45
 
44
- payload = {:data => 'test'}
46
+ payload = { data: 'test' }
45
47
 
46
48
  # IMPORTANT: set nil as password parameter
47
49
  token = JWT.encode payload, nil, 'none'
48
50
 
49
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.
51
+ # eyJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.
50
52
  puts token
51
53
 
52
54
  # Set password to nil and validation to false otherwise this won't work
@@ -55,14 +57,15 @@ decoded_token = JWT.decode token, nil, false
55
57
  # Array
56
58
  # [
57
59
  # {"data"=>"test"}, # payload
58
- # {"typ"=>"JWT", "alg"=>"none"} # header
60
+ # {"alg"=>"none"} # header
59
61
  # ]
60
62
  puts decoded_token
61
63
  ```
62
64
 
63
- **HMAC** (default: HS256)
65
+ **HMAC**
64
66
 
65
- * HS256 - HMAC using SHA-256 hash algorithm (default)
67
+ * HS256 - HMAC using SHA-256 hash algorithm
68
+ * HS512256 - HMAC using SHA-512-256 hash algorithm (only available with RbNaCl; see note below)
66
69
  * HS384 - HMAC using SHA-384 hash algorithm
67
70
  * HS512 - HMAC using SHA-512 hash algorithm
68
71
 
@@ -71,19 +74,40 @@ hmac_secret = 'my$ecretK3y'
71
74
 
72
75
  token = JWT.encode payload, hmac_secret, 'HS256'
73
76
 
74
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.ZxW8go9hz3ETCSfxFxpwSkYg_602gOPKearsf6DsxgY
77
+ # eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.pNIWIL34Jo13LViZAJACzK6Yf0qnvT_BuwOxiMCPE-Y
78
+ puts token
79
+
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
75
93
  puts token
76
94
 
77
- decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' }
95
+ decoded_token = JWT.decode token, nil, true, { algorithm: 'HS256' }
78
96
 
79
97
  # Array
80
98
  # [
81
99
  # {"data"=>"test"}, # payload
82
- # {"typ"=>"JWT", "alg"=>"HS256"} # header
100
+ # {"alg"=>"HS256"} # header
83
101
  # ]
84
102
  puts decoded_token
85
103
  ```
86
104
 
105
+ Note: If [RbNaCl](https://github.com/cryptosphere/rbnacl) is loadable, ruby-jwt will use it for HMAC-SHA256, HMAC-SHA512-256, and HMAC-SHA512. RbNaCl enforces a maximum key size of 32 bytes for these algorithms.
106
+
107
+ [RbNaCl](https://github.com/cryptosphere/rbnacl) requires
108
+ [libsodium](https://github.com/jedisct1/libsodium), it can be installed
109
+ on MacOS with `brew install libsodium`.
110
+
87
111
  **RSA**
88
112
 
89
113
  * RS256 - RSA using SHA-256 hash algorithm
@@ -96,15 +120,15 @@ rsa_public = rsa_private.public_key
96
120
 
97
121
  token = JWT.encode payload, rsa_private, 'RS256'
98
122
 
99
- # 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
100
124
  puts token
101
125
 
102
- decoded_token = JWT.decode token, rsa_public, true, { :algorithm => 'RS256' }
126
+ decoded_token = JWT.decode token, rsa_public, true, { algorithm: 'RS256' }
103
127
 
104
128
  # Array
105
129
  # [
106
130
  # {"data"=>"test"}, # payload
107
- # {"typ"=>"JWT", "alg"=>"RS256"} # header
131
+ # {"alg"=>"RS256"} # header
108
132
  # ]
109
133
  puts decoded_token
110
134
  ```
@@ -123,22 +147,78 @@ ecdsa_public.private_key = nil
123
147
 
124
148
  token = JWT.encode payload, ecdsa_key, 'ES256'
125
149
 
126
- # eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJ0ZXN0IjoiZGF0YSJ9.MEQCIAtShrxRwP1L9SapqaT4f7hajDJH4t_rfm-YlZcNDsBNAiB64M4-JRfyS8nRMlywtQ9lHbvvec9U54KznzOe1YxTyA
150
+ # eyJhbGciOiJFUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.AlLW--kaF7EX1NMX9WJRuIW8NeRJbn2BLXHns7Q5TZr7Hy3lF6MOpMlp7GoxBFRLISQ6KrD0CJOrR8aogEsPeg
127
151
  puts token
128
152
 
129
- decoded_token = JWT.decode token, ecdsa_public, true, { :algorithm => 'ES256' }
153
+ decoded_token = JWT.decode token, ecdsa_public, true, { algorithm: 'ES256' }
130
154
 
131
155
  # Array
132
156
  # [
133
157
  # {"test"=>"data"}, # payload
134
- # {"typ"=>"JWT", "alg"=>"ES256"} # header
158
+ # {"alg"=>"ES256"} # header
135
159
  # ]
136
160
  puts decoded_token
137
161
  ```
138
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
+
139
192
  **RSASSA-PSS**
140
193
 
141
- 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
+ ```
142
222
 
143
223
  ## Support for reserved claim names
144
224
  JSON Web Token defines some reserved claim names and defines how they should be
@@ -152,6 +232,38 @@ used. JWT supports these reserved claim names:
152
232
  - 'iat' (Issued At) Claim
153
233
  - 'sub' (Subject) Claim
154
234
 
235
+ ## Add custom header fields
236
+ Ruby-jwt gem supports custom [header fields](https://tools.ietf.org/html/rfc7519#section-5)
237
+ To add custom header fields you need to pass `header_fields` parameter
238
+
239
+ ```ruby
240
+ token = JWT.encode payload, key, algorithm='HS256', header_fields={}
241
+ ```
242
+
243
+ **Example:**
244
+
245
+ ```ruby
246
+ require 'jwt'
247
+
248
+ payload = { data: 'test' }
249
+
250
+ # IMPORTANT: set nil as password parameter
251
+ token = JWT.encode payload, nil, 'none', { typ: 'JWT' }
252
+
253
+ # eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJkYXRhIjoidGVzdCJ9.
254
+ puts token
255
+
256
+ # Set password to nil and validation to false otherwise this won't work
257
+ decoded_token = JWT.decode token, nil, false
258
+
259
+ # Array
260
+ # [
261
+ # {"data"=>"test"}, # payload
262
+ # {"typ"=>"JWT", "alg"=>"none"} # header
263
+ # ]
264
+ puts decoded_token
265
+ ```
266
+
155
267
  ### Expiration Time Claim
156
268
 
157
269
  From [Oauth JSON Web Token 4.1.4. "exp" (Expiration Time) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.4):
@@ -162,12 +274,12 @@ From [Oauth JSON Web Token 4.1.4. "exp" (Expiration Time) Claim](https://tools.i
162
274
 
163
275
  ```ruby
164
276
  exp = Time.now.to_i + 4 * 3600
165
- exp_payload = { :data => 'data', :exp => exp }
277
+ exp_payload = { data: 'data', exp: exp }
166
278
 
167
279
  token = JWT.encode exp_payload, hmac_secret, 'HS256'
168
280
 
169
281
  begin
170
- decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' }
282
+ decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
171
283
  rescue JWT::ExpiredSignature
172
284
  # Handle expired token, e.g. logout user or deny access
173
285
  end
@@ -179,14 +291,14 @@ end
179
291
  exp = Time.now.to_i - 10
180
292
  leeway = 30 # seconds
181
293
 
182
- exp_payload = { :data => 'data', :exp => exp }
294
+ exp_payload = { data: 'data', exp: exp }
183
295
 
184
296
  # build expired token
185
297
  token = JWT.encode exp_payload, hmac_secret, 'HS256'
186
298
 
187
299
  begin
188
300
  # add leeway to ensure the token is still accepted
189
- decoded_token = JWT.decode token, hmac_secret, true, { :leeway => leeway, :algorithm => 'HS256' }
301
+ decoded_token = JWT.decode token, hmac_secret, true, { exp_leeway: leeway, algorithm: 'HS256' }
190
302
  rescue JWT::ExpiredSignature
191
303
  # Handle expired token, e.g. logout user or deny access
192
304
  end
@@ -202,12 +314,12 @@ From [Oauth JSON Web Token 4.1.5. "nbf" (Not Before) Claim](https://tools.ietf.o
202
314
 
203
315
  ```ruby
204
316
  nbf = Time.now.to_i - 3600
205
- nbf_payload = { :data => 'data', :nbf => nbf }
317
+ nbf_payload = { data: 'data', nbf: nbf }
206
318
 
207
319
  token = JWT.encode nbf_payload, hmac_secret, 'HS256'
208
320
 
209
321
  begin
210
- decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' }
322
+ decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
211
323
  rescue JWT::ImmatureSignature
212
324
  # Handle invalid token, e.g. logout user or deny access
213
325
  end
@@ -219,14 +331,14 @@ end
219
331
  nbf = Time.now.to_i + 10
220
332
  leeway = 30
221
333
 
222
- nbf_payload = { :data => 'data', :nbf => nbf }
334
+ nbf_payload = { data: 'data', nbf: nbf }
223
335
 
224
336
  # build expired token
225
337
  token = JWT.encode nbf_payload, hmac_secret, 'HS256'
226
338
 
227
339
  begin
228
340
  # add leeway to ensure the token is valid
229
- decoded_token = JWT.decode token, hmac_secret, true, { :leeway => leeway, :algorithm => 'HS256' }
341
+ decoded_token = JWT.decode token, hmac_secret, true, { nbf_leeway: leeway, algorithm: 'HS256' }
230
342
  rescue JWT::ImmatureSignature
231
343
  # Handle invalid token, e.g. logout user or deny access
232
344
  end
@@ -238,15 +350,17 @@ From [Oauth JSON Web Token 4.1.1. "iss" (Issuer) Claim](https://tools.ietf.org/h
238
350
 
239
351
  > The `iss` (issuer) claim identifies the principal that issued the JWT. The processing of this claim is generally application specific. The `iss` value is a case-sensitive string containing a ***StringOrURI*** value. Use of this claim is OPTIONAL.
240
352
 
353
+ You can pass multiple allowed issuers as an Array, verification will pass if one of them matches the `iss` value in the payload.
354
+
241
355
  ```ruby
242
356
  iss = 'My Awesome Company Inc. or https://my.awesome.website/'
243
- iss_payload = { :data => 'data', :iss => iss }
357
+ iss_payload = { data: 'data', iss: iss }
244
358
 
245
359
  token = JWT.encode iss_payload, hmac_secret, 'HS256'
246
360
 
247
361
  begin
248
362
  # Add iss to the validation to check if the token has been manipulated
249
- 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' }
250
364
  rescue JWT::InvalidIssuerError
251
365
  # Handle invalid token, e.g. logout user or deny access
252
366
  end
@@ -260,13 +374,13 @@ From [Oauth JSON Web Token 4.1.3. "aud" (Audience) Claim](https://tools.ietf.org
260
374
 
261
375
  ```ruby
262
376
  aud = ['Young', 'Old']
263
- aud_payload = { :data => 'data', :aud => aud }
377
+ aud_payload = { data: 'data', aud: aud }
264
378
 
265
379
  token = JWT.encode aud_payload, hmac_secret, 'HS256'
266
380
 
267
381
  begin
268
382
  # Add aud to the validation to check if the token has been manipulated
269
- 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' }
270
384
  rescue JWT::InvalidAudError
271
385
  # Handle invalid token, e.g. logout user or deny access
272
386
  puts 'Audience Error'
@@ -283,37 +397,38 @@ From [Oauth JSON Web Token 4.1.7. "jti" (JWT ID) Claim](https://tools.ietf.org/h
283
397
  # Use the secret and iat to create a unique key per request to prevent replay attacks
284
398
  jti_raw = [hmac_secret, iat].join(':').to_s
285
399
  jti = Digest::MD5.hexdigest(jti_raw)
286
- jti_payload = { :data => 'data', :iat => iat, :jti => jti }
400
+ jti_payload = { data: 'data', iat: iat, jti: jti }
287
401
 
288
402
  token = JWT.encode jti_payload, hmac_secret, 'HS256'
289
403
 
290
404
  begin
291
405
  # If :verify_jti is true, validation will pass if a JTI is present
292
- #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' }
293
407
  # Alternatively, pass a proc with your own code to check if the JTI has already been used
294
- 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' }
295
409
  rescue JWT::InvalidJtiError
296
410
  # Handle invalid token, e.g. logout user or deny access
297
411
  puts 'Error'
298
412
  end
299
-
300
413
  ```
301
414
 
302
415
  ### Issued At Claim
303
416
 
304
417
  From [Oauth JSON Web Token 4.1.6. "iat" (Issued At) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.6):
305
418
 
306
- > 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.
420
+
421
+ **Handle Issued At Claim**
307
422
 
308
423
  ```ruby
309
424
  iat = Time.now.to_i
310
- iat_payload = { :data => 'data', :iat => iat }
425
+ iat_payload = { data: 'data', iat: iat }
311
426
 
312
427
  token = JWT.encode iat_payload, hmac_secret, 'HS256'
313
428
 
314
429
  begin
315
430
  # Add iat to the validation to check if the token has been manipulated
316
- decoded_token = JWT.decode token, hmac_secret, true, { :verify_iat => true, :algorithm => 'HS256' }
431
+ decoded_token = JWT.decode token, hmac_secret, true, { verify_iat: true, algorithm: 'HS256' }
317
432
  rescue JWT::InvalidIatError
318
433
  # Handle invalid token, e.g. logout user or deny access
319
434
  end
@@ -327,18 +442,43 @@ From [Oauth JSON Web Token 4.1.2. "sub" (Subject) Claim](https://tools.ietf.org/
327
442
 
328
443
  ```ruby
329
444
  sub = 'Subject'
330
- sub_payload = { :data => 'data', :sub => sub }
445
+ sub_payload = { data: 'data', sub: sub }
331
446
 
332
447
  token = JWT.encode sub_payload, hmac_secret, 'HS256'
333
448
 
334
449
  begin
335
450
  # Add sub to the validation to check if the token has been manipulated
336
- 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' }
337
452
  rescue JWT::InvalidSubError
338
453
  # Handle invalid token, e.g. logout user or deny access
339
454
  end
340
455
  ```
341
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
+
342
482
  # Development and Tests
343
483
 
344
484
  We depend on [Bundler](http://rubygems.org/gems/bundler) for defining gemspec and performing releases to rubygems.org, which can be done with
@@ -357,30 +497,8 @@ bundle exec rspec
357
497
 
358
498
  ## Contributors
359
499
 
360
- * Jordan Brough <github.jordanb@xoxy.net>
361
- * Ilya Zhitomirskiy <ilya@joindiaspora.com>
362
- * Daniel Grippi <daniel@joindiaspora.com>
363
- * Jeff Lindsay <progrium@gmail.com>
364
- * Bob Aman <bob@sporkmonger.com>
365
- * Micah Gates <github@mgates.com>
366
- * Rob Wygand <rob@wygand.com>
367
- * Ariel Salomon (Oscil8)
368
- * Paul Battley <pbattley@gmail.com>
369
- * Zane Shannon [@zshannon](https://github.com/zshannon)
370
- * Brian Fletcher [@punkle](https://github.com/punkle)
371
- * Alex [@ZhangHanDong](https://github.com/ZhangHanDong)
372
- * John Downey [@jtdowney](https://github.com/jtdowney)
373
- * Adam Greene [@skippy](https://github.com/skippy)
374
- * Tim Rudat [@excpt](https://github.com/excpt) <timrudat@gmail.com> - Maintainer
500
+ See `AUTHORS` file.
375
501
 
376
502
  ## License
377
503
 
378
- MIT
379
-
380
- Copyright (c) 2011 Jeff Lindsay
381
-
382
- 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:
383
-
384
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
385
-
386
- 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.