jwt 2.10.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +61 -32
- data/CODE_OF_CONDUCT.md +14 -14
- data/CONTRIBUTING.md +9 -10
- data/README.md +132 -151
- data/UPGRADING.md +47 -0
- data/lib/jwt/base64.rb +1 -10
- data/lib/jwt/claims/numeric.rb +0 -32
- data/lib/jwt/claims.rb +0 -7
- data/lib/jwt/configuration/container.rb +0 -1
- data/lib/jwt/decode.rb +10 -14
- data/lib/jwt/encoded_token.rb +71 -4
- data/lib/jwt/error.rb +0 -3
- data/lib/jwt/jwa/ecdsa.rb +0 -4
- data/lib/jwt/jwa/hmac.rb +0 -4
- data/lib/jwt/jwa/ps.rb +1 -0
- data/lib/jwt/jwa/rsa.rb +1 -0
- data/lib/jwt/jwa/signing_algorithm.rb +0 -1
- data/lib/jwt/jwa.rb +1 -26
- data/lib/jwt/jwk/ec.rb +1 -5
- data/lib/jwt/jwk/hmac.rb +3 -3
- data/lib/jwt/jwk/key_finder.rb +14 -1
- data/lib/jwt/jwk/rsa.rb +4 -1
- data/lib/jwt/jwk.rb +0 -1
- data/lib/jwt/token.rb +22 -3
- data/lib/jwt/version.rb +4 -20
- data/lib/jwt.rb +1 -7
- data/ruby-jwt.gemspec +1 -0
- metadata +19 -14
- data/lib/jwt/claims/verification_methods.rb +0 -20
- data/lib/jwt/claims_validator.rb +0 -18
- data/lib/jwt/deprecations.rb +0 -49
- data/lib/jwt/jwa/compat.rb +0 -32
- data/lib/jwt/jwa/eddsa.rb +0 -35
- data/lib/jwt/jwa/hmac_rbnacl.rb +0 -50
- data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +0 -47
- data/lib/jwt/jwa/wrapper.rb +0 -44
- data/lib/jwt/jwk/okp_rbnacl.rb +0 -109
- data/lib/jwt/verify.rb +0 -40
data/README.md
CHANGED
@@ -2,9 +2,8 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/jwt)
|
4
4
|
[](https://github.com/jwt/ruby-jwt/actions)
|
5
|
-
[](https://codeclimate.com/github/jwt/ruby-jwt)
|
5
|
+
[](https://qlty.sh/gh/jwt/projects/ruby-jwt)
|
6
|
+
[](https://qlty.sh/gh/jwt/projects/ruby-jwt)
|
8
7
|
|
9
8
|
A ruby implementation of the [RFC 7519 OAuth JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519) standard.
|
10
9
|
|
@@ -14,50 +13,34 @@ See [CHANGELOG.md](CHANGELOG.md) for a complete set of changes.
|
|
14
13
|
|
15
14
|
## Upcoming breaking changes
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
- The indirect dependency to [rbnacl](https://github.com/RubyCrypto/rbnacl) will be removed:
|
20
|
-
- Support for the non-standard SHA512256 algorithm will be removed.
|
21
|
-
- Support for Ed25519 will be moved to a [separate gem](https://github.com/anakinj/jwt-eddsa) for better dependency handling.
|
22
|
-
|
23
|
-
- Base64 decoding will no longer fallback on the looser RFC 2045.
|
24
|
-
|
25
|
-
- Claim verification has been [split into separate classes](https://github.com/jwt/ruby-jwt/pull/605) and has [a new api](https://github.com/jwt/ruby-jwt/pull/626) and lead to the following deprecations:
|
26
|
-
- The `::JWT::ClaimsValidator` class will be removed in favor of the functionality provided by `::JWT::Claims`.
|
27
|
-
- The `::JWT::Claims::verify!` method will be removed in favor of `::JWT::Claims::verify_payload!`.
|
28
|
-
- The `::JWT::JWA.create` method will be removed.
|
29
|
-
- The `::JWT::Verify` class will be removed in favor of the functionality provided by `::JWT::Claims`.
|
30
|
-
- Calling `::JWT::Claims::Numeric.new` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.
|
31
|
-
- Calling `::JWT::Claims::Numeric.verify!` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.
|
32
|
-
|
33
|
-
- The internal algorithms were [restructured](https://github.com/jwt/ruby-jwt/pull/607) to support extensions from separate libraries. The changes lead to a few deprecations and new requirements:
|
34
|
-
- The `sign` and `verify` static methods on all the algorithms (`::JWT::JWA`) will be removed.
|
35
|
-
- Custom algorithms are expected to include the `JWT::JWA::SigningAlgorithm` module.
|
16
|
+
Check out breaking changes in the upcoming **version 3.0** from the [upgrade guide](UPGRADING.md)
|
36
17
|
|
37
18
|
## Sponsors
|
38
19
|
|
39
|
-
|Logo|Message|
|
40
|
-
|
41
|
-
|
20
|
+
| Logo | Message |
|
21
|
+
| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
22
|
+
|  | 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) |
|
42
23
|
|
43
24
|
## Installing
|
44
25
|
|
45
|
-
### Using Rubygems
|
26
|
+
### Using Rubygems
|
46
27
|
|
47
28
|
```bash
|
48
29
|
gem install jwt
|
49
30
|
```
|
50
31
|
|
51
|
-
### Using Bundler
|
32
|
+
### Using Bundler
|
52
33
|
|
53
34
|
Add the following to your Gemfile
|
54
|
-
|
35
|
+
|
36
|
+
```bash
|
55
37
|
gem 'jwt'
|
56
38
|
```
|
57
39
|
|
58
40
|
And run `bundle install`
|
59
41
|
|
60
42
|
Finally require the gem in your application
|
43
|
+
|
61
44
|
```ruby
|
62
45
|
require 'jwt'
|
63
46
|
```
|
@@ -66,32 +49,15 @@ require 'jwt'
|
|
66
49
|
|
67
50
|
The jwt gem natively supports the NONE, HMAC, RSASSA, ECDSA and RSASSA-PSS algorithms via the openssl library. The gem can be extended with additional or alternative implementations of the algorithms via extensions.
|
68
51
|
|
69
|
-
Additionally the EdDSA algorithm is supported via a [
|
52
|
+
Additionally the EdDSA algorithm is supported via a the [jwt-eddsa gem](https://rubygems.org/gems/jwt-eddsa).
|
70
53
|
|
71
54
|
For safe 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/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**
|
72
55
|
|
73
|
-
See
|
74
|
-
|
75
|
-
### Deprecation warnings
|
76
|
-
|
77
|
-
Deprecation warnings are logged once (`:once` option) by default to avoid spam in logs. Other options are `:silent` to completely silence warnings and `:warn` to log every time a deprecated path is executed.
|
78
|
-
|
79
|
-
```ruby
|
80
|
-
JWT.configuration.deprecation_warnings = :warn # default is :once
|
81
|
-
```
|
82
|
-
|
83
|
-
### Base64 decoding
|
84
|
-
|
85
|
-
In the past the gem has been supporting the Base64 decoding specified in [RFC2045](https://www.rfc-editor.org/rfc/rfc2045) allowing newlines and blanks in the base64 encoded payload. In future versions base64 decoding will be stricter and only comply to [RFC4648](https://www.rfc-editor.org/rfc/rfc4648).
|
86
|
-
|
87
|
-
The stricter base64 decoding when processing tokens can be done via the `strict_base64_decoding` configuration accessor.
|
88
|
-
```ruby
|
89
|
-
JWT.configuration.strict_base64_decoding = true # default is false
|
90
|
-
```
|
56
|
+
See [JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1)
|
91
57
|
|
92
58
|
### **NONE**
|
93
59
|
|
94
|
-
|
60
|
+
- none - unsigned token
|
95
61
|
|
96
62
|
```ruby
|
97
63
|
|
@@ -116,9 +82,9 @@ puts decoded_token
|
|
116
82
|
|
117
83
|
### **HMAC**
|
118
84
|
|
119
|
-
|
120
|
-
|
121
|
-
|
85
|
+
- HS256 - HMAC using SHA-256 hash algorithm
|
86
|
+
- HS384 - HMAC using SHA-384 hash algorithm
|
87
|
+
- HS512 - HMAC using SHA-512 hash algorithm
|
122
88
|
|
123
89
|
```ruby
|
124
90
|
# The secret must be a string. With OpenSSL 3.0/openssl gem `<3.0.1`, JWT::DecodeError will be raised if it isn't provided.
|
@@ -141,9 +107,9 @@ puts decoded_token
|
|
141
107
|
|
142
108
|
### **RSA**
|
143
109
|
|
144
|
-
|
145
|
-
|
146
|
-
|
110
|
+
- RS256 - RSA using SHA-256 hash algorithm
|
111
|
+
- RS384 - RSA using SHA-384 hash algorithm
|
112
|
+
- RS512 - RSA using SHA-512 hash algorithm
|
147
113
|
|
148
114
|
```ruby
|
149
115
|
rsa_private = OpenSSL::PKey::RSA.generate(2048)
|
@@ -166,10 +132,10 @@ puts decoded_token
|
|
166
132
|
|
167
133
|
### **ECDSA**
|
168
134
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
135
|
+
- ES256 - ECDSA using P-256 and SHA-256
|
136
|
+
- ES384 - ECDSA using P-384 and SHA-384
|
137
|
+
- ES512 - ECDSA using P-521 and SHA-512
|
138
|
+
- ES256K - ECDSA using P-256K and SHA-256
|
173
139
|
|
174
140
|
```ruby
|
175
141
|
ecdsa_key = OpenSSL::PKey::EC.generate('prime256v1')
|
@@ -189,49 +155,18 @@ decoded_token = JWT.decode(token, ecdsa_key, true, { algorithm: 'ES256' })
|
|
189
155
|
puts decoded_token
|
190
156
|
```
|
191
157
|
|
192
|
-
### **
|
193
|
-
|
194
|
-
In order to use this algorithm you need to add the `RbNaCl` gem to you `Gemfile`.
|
195
|
-
|
196
|
-
```ruby
|
197
|
-
gem 'rbnacl'
|
198
|
-
```
|
199
|
-
|
200
|
-
For more detailed installation instruction check the official [repository](https://github.com/RubyCrypto/rbnacl) on GitHub.
|
201
|
-
|
202
|
-
* ED25519
|
203
|
-
|
204
|
-
```ruby
|
205
|
-
private_key = RbNaCl::Signatures::Ed25519::SigningKey.new('abcdefghijklmnopqrstuvwxyzABCDEF')
|
206
|
-
public_key = private_key.verify_key
|
207
|
-
token = JWT.encode payload, private_key, 'ED25519'
|
208
|
-
|
209
|
-
# eyJhbGciOiJFRDI1NTE5In0.eyJkYXRhIjoidGVzdCJ9.6xIztXyOupskddGA_RvKU76V9b2dCQUYhoZEVFnRimJoPYIzZ2Fm47CWw8k2NTCNpgfAuxg9OXjaiVK7MvrbCQ
|
210
|
-
puts token
|
211
|
-
|
212
|
-
decoded_token = JWT.decode token, public_key, true, { algorithm: 'ED25519' }
|
213
|
-
# Array
|
214
|
-
# [
|
215
|
-
# {"test"=>"data"}, # payload
|
216
|
-
# {"alg"=>"ED25519"} # header
|
217
|
-
# ]
|
158
|
+
### **EdDSA**
|
218
159
|
|
219
|
-
|
160
|
+
This algorithm has since version 3.0 been moved to the [jwt-eddsa gem](https://rubygems.org/gems/jwt-eddsa).
|
220
161
|
|
221
162
|
### **RSASSA-PSS**
|
222
163
|
|
223
|
-
|
164
|
+
- PS256 - RSASSA-PSS using SHA-256 hash algorithm
|
165
|
+
- PS384 - RSASSA-PSS using SHA-384 hash algorithm
|
166
|
+
- PS512 - RSASSA-PSS using SHA-512 hash algorithm
|
224
167
|
|
225
168
|
```ruby
|
226
|
-
|
227
|
-
```
|
228
|
-
|
229
|
-
* PS256 - RSASSA-PSS using SHA-256 hash algorithm
|
230
|
-
* PS384 - RSASSA-PSS using SHA-384 hash algorithm
|
231
|
-
* PS512 - RSASSA-PSS using SHA-512 hash algorithm
|
232
|
-
|
233
|
-
```ruby
|
234
|
-
rsa_private = OpenSSL::PKey::RSA.generate 2048
|
169
|
+
rsa_private = OpenSSL::PKey::RSA.generate(2048)
|
235
170
|
rsa_public = rsa_private.public_key
|
236
171
|
|
237
172
|
token = JWT.encode(payload, rsa_private, 'PS256')
|
@@ -249,37 +184,6 @@ decoded_token = JWT.decode(token, rsa_public, true, { algorithm: 'PS256' })
|
|
249
184
|
puts decoded_token
|
250
185
|
```
|
251
186
|
|
252
|
-
### Add custom header fields
|
253
|
-
Ruby-jwt gem supports custom [header fields](https://tools.ietf.org/html/rfc7519#section-5)
|
254
|
-
To add custom header fields you need to pass `header_fields` parameter
|
255
|
-
|
256
|
-
```ruby
|
257
|
-
token = JWT.encode(payload, key, algorithm='HS256', header_fields={})
|
258
|
-
```
|
259
|
-
|
260
|
-
**Example:**
|
261
|
-
|
262
|
-
```ruby
|
263
|
-
|
264
|
-
payload = { data: 'test' }
|
265
|
-
|
266
|
-
# IMPORTANT: set nil as password parameter
|
267
|
-
token = JWT.encode(payload, nil, 'none', { typ: 'JWT' })
|
268
|
-
|
269
|
-
# eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJkYXRhIjoidGVzdCJ9.
|
270
|
-
puts token
|
271
|
-
|
272
|
-
# Set password to nil and validation to false otherwise this won't work
|
273
|
-
decoded_token = JWT.decode(token, nil, false)
|
274
|
-
|
275
|
-
# Array
|
276
|
-
# [
|
277
|
-
# {"data"=>"test"}, # payload
|
278
|
-
# {"typ"=>"JWT", "alg"=>"none"} # header
|
279
|
-
# ]
|
280
|
-
puts decoded_token
|
281
|
-
```
|
282
|
-
|
283
187
|
### **Custom algorithms**
|
284
188
|
|
285
189
|
When encoding or decoding a token, you can pass in a custom object through the `algorithm` option to handle signing or verification. This custom object must include or extend the `JWT::JWA::SigningAlgorithm` module and implement certain methods:
|
@@ -289,7 +193,6 @@ When encoding or decoding a token, you can pass in a custom object through the `
|
|
289
193
|
|
290
194
|
For customization options check the details from `JWT::JWA::SigningAlgorithm`.
|
291
195
|
|
292
|
-
|
293
196
|
```ruby
|
294
197
|
module CustomHS512Algorithm
|
295
198
|
extend JWT::JWA::SigningAlgorithm
|
@@ -311,10 +214,44 @@ token = ::JWT.encode({'pay' => 'load'}, 'secret', CustomHS512Algorithm)
|
|
311
214
|
payload, header = ::JWT.decode(token, 'secret', true, algorithm: CustomHS512Algorithm)
|
312
215
|
```
|
313
216
|
|
217
|
+
### Add custom header fields
|
218
|
+
|
219
|
+
The ruby-jwt gem supports custom [header fields](https://tools.ietf.org/html/rfc7519#section-5)
|
220
|
+
To add custom header fields you need to pass `header_fields` parameter
|
221
|
+
|
222
|
+
```ruby
|
223
|
+
token = JWT.encode(payload, key, 'HS256', {})
|
224
|
+
```
|
225
|
+
|
226
|
+
**Example:**
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
|
230
|
+
payload = { data: 'test' }
|
231
|
+
|
232
|
+
# IMPORTANT: set nil as password parameter
|
233
|
+
token = JWT.encode(payload, nil, 'none', { typ: 'JWT' })
|
234
|
+
|
235
|
+
# eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJkYXRhIjoidGVzdCJ9.
|
236
|
+
puts token
|
237
|
+
|
238
|
+
# Set password to nil and validation to false otherwise this won't work
|
239
|
+
decoded_token = JWT.decode(token, nil, false)
|
240
|
+
|
241
|
+
# Array
|
242
|
+
# [
|
243
|
+
# {"data"=>"test"}, # payload
|
244
|
+
# {"typ"=>"JWT", "alg"=>"none"} # header
|
245
|
+
# ]
|
246
|
+
puts decoded_token
|
247
|
+
```
|
248
|
+
|
314
249
|
## `JWT::Token` and `JWT::EncodedToken`
|
315
250
|
|
316
251
|
The `JWT::Token` and `JWT::EncodedToken` classes can be used to manage your JWTs.
|
317
252
|
|
253
|
+
### Signing and encoding a token
|
254
|
+
|
318
255
|
```ruby
|
319
256
|
token = JWT::Token.new(payload: { exp: Time.now.to_i + 60, jti: '1234', sub: "my-subject" }, header: { kid: 'hmac' })
|
320
257
|
token.sign!(algorithm: 'HS256', key: "secret")
|
@@ -322,7 +259,10 @@ token.sign!(algorithm: 'HS256', key: "secret")
|
|
322
259
|
token.jwt # => "eyJhbGciOiJIUzI1N..."
|
323
260
|
```
|
324
261
|
|
325
|
-
|
262
|
+
### Verifying and decoding a token
|
263
|
+
|
264
|
+
The `JWT::EncodedToken` can be used as a token object that allows verification of signatures and claims.
|
265
|
+
|
326
266
|
```ruby
|
327
267
|
encoded_token = JWT::EncodedToken.new(token.jwt)
|
328
268
|
|
@@ -335,6 +275,48 @@ encoded_token.payload # => { 'exp'=>1234, 'jti'=>'1234", 'sub'=>'my-subject' }
|
|
335
275
|
encoded_token.header # {'kid'=>'hmac', 'alg'=>'HS256'}
|
336
276
|
```
|
337
277
|
|
278
|
+
The `JWT::EncodedToken#verify!` method can be used to verify signature and claim verification in one go. The `exp` claim is verified by default.
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
encoded_token = JWT::EncodedToken.new(token.jwt)
|
282
|
+
encoded_token.verify!(signature: {algorithm: 'HS256', key: "secret"})
|
283
|
+
encoded_token.payload # => { 'exp'=>1234, 'jti'=>'1234", 'sub'=>'my-subject' }
|
284
|
+
encoded_token.header # {'kid'=>'hmac', 'alg'=>'HS256'}
|
285
|
+
```
|
286
|
+
|
287
|
+
#### Keyfinders
|
288
|
+
|
289
|
+
A keyfinder can be used to verify a signature. A keyfinder is an object responding to the `#call` method. The method expects to receive one argument, which is the token to be verified.
|
290
|
+
|
291
|
+
An example on using the built-in JWK keyfinder.
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
# Create and sign a token
|
295
|
+
jwk = JWT::JWK.new(OpenSSL::PKey::RSA.generate(2048))
|
296
|
+
token = JWT::Token.new(payload: { pay: 'load' }, header: { kid: jwk.kid })
|
297
|
+
token.sign!(algorithm: 'RS256', key: jwk.signing_key)
|
298
|
+
|
299
|
+
# Create keyfinder object, verify and decode token
|
300
|
+
key_finder = JWT::JWK::KeyFinder.new(jwks: JWT::JWK::Set.new(jwk))
|
301
|
+
encoded_token = JWT::EncodedToken.new(token.jwt)
|
302
|
+
encoded_token.verify!(signature: { algorithm: 'RS256', key_finder: key_finder})
|
303
|
+
encoded_token.payload # => { 'pay' => 'load' }
|
304
|
+
```
|
305
|
+
|
306
|
+
Using a custom keyfinder proc.
|
307
|
+
|
308
|
+
```ruby
|
309
|
+
# Create and sign a token
|
310
|
+
key = OpenSSL::PKey::RSA.generate(2048)
|
311
|
+
token = JWT::Token.new(payload: { pay: 'load' })
|
312
|
+
token.sign!(algorithm: 'RS256', key: key)
|
313
|
+
|
314
|
+
# Verify and decode token
|
315
|
+
encoded_token = JWT::EncodedToken.new(token.jwt)
|
316
|
+
encoded_token.verify!(signature: { algorithm: 'RS256', key_finder: ->(_token){ key.public_key }})
|
317
|
+
encoded_token.payload # => { 'pay' => 'load' }
|
318
|
+
```
|
319
|
+
|
338
320
|
### Detached payload
|
339
321
|
|
340
322
|
The `::JWT::Token#detach_payload!` method can be use to detach the payload from the JWT.
|
@@ -361,21 +343,19 @@ encoded_token.payload # => {"pay"=>"load"}
|
|
361
343
|
JSON Web Token defines some reserved claim names and defines how they should be
|
362
344
|
used. JWT supports these reserved claim names:
|
363
345
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
346
|
+
- 'exp' (Expiration Time) Claim
|
347
|
+
- 'nbf' (Not Before Time) Claim
|
348
|
+
- 'iss' (Issuer) Claim
|
349
|
+
- 'aud' (Audience) Claim
|
350
|
+
- 'jti' (JWT ID) Claim
|
351
|
+
- 'iat' (Issued At) Claim
|
352
|
+
- 'sub' (Subject) Claim
|
371
353
|
|
372
354
|
### Expiration Time Claim
|
373
355
|
|
374
356
|
From [Oauth JSON Web Token 4.1.4. "exp" (Expiration Time) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.4):
|
375
357
|
|
376
|
-
> The `exp` (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. The processing of the `exp` claim requires that the current date/time MUST be before the expiration date/time listed in the `exp` claim. Implementers MAY provide for some small `leeway`, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a
|
377
|
-
|
378
|
-
**Handle Expiration Claim**
|
358
|
+
> The `exp` (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. The processing of the `exp` claim requires that the current date/time MUST be before the expiration date/time listed in the `exp` claim. Implementers MAY provide for some small `leeway`, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a **_NumericDate_** value. Use of this claim is OPTIONAL.
|
379
359
|
|
380
360
|
```ruby
|
381
361
|
exp = Time.now.to_i + 4 * 3600
|
@@ -391,12 +371,13 @@ end
|
|
391
371
|
```
|
392
372
|
|
393
373
|
The Expiration Claim verification can be disabled.
|
374
|
+
|
394
375
|
```ruby
|
395
376
|
# Decode token without raising JWT::ExpiredSignature error
|
396
377
|
JWT.decode(token, hmac_secret, true, { verify_expiration: false, algorithm: 'HS256' })
|
397
378
|
```
|
398
379
|
|
399
|
-
|
380
|
+
Leeway and the exp claim.
|
400
381
|
|
401
382
|
```ruby
|
402
383
|
exp = Time.now.to_i - 10
|
@@ -419,9 +400,7 @@ end
|
|
419
400
|
|
420
401
|
From [Oauth JSON Web Token 4.1.5. "nbf" (Not Before) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.5):
|
421
402
|
|
422
|
-
> The `nbf` (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. The processing of the `nbf` claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the `nbf` claim. Implementers MAY provide for some small `leeway`, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a
|
423
|
-
|
424
|
-
**Handle Not Before Claim**
|
403
|
+
> The `nbf` (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. The processing of the `nbf` claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the `nbf` claim. Implementers MAY provide for some small `leeway`, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a **_NumericDate_** value. Use of this claim is OPTIONAL.
|
425
404
|
|
426
405
|
```ruby
|
427
406
|
nbf = Time.now.to_i - 3600
|
@@ -437,12 +416,13 @@ end
|
|
437
416
|
```
|
438
417
|
|
439
418
|
The Not Before Claim verification can be disabled.
|
419
|
+
|
440
420
|
```ruby
|
441
421
|
# Decode token without raising JWT::ImmatureSignature error
|
442
422
|
JWT.decode(token, hmac_secret, true, { verify_not_before: false, algorithm: 'HS256' })
|
443
423
|
```
|
444
424
|
|
445
|
-
|
425
|
+
Leeway and the nbf claim.
|
446
426
|
|
447
427
|
```ruby
|
448
428
|
nbf = Time.now.to_i + 10
|
@@ -465,7 +445,7 @@ end
|
|
465
445
|
|
466
446
|
From [Oauth JSON Web Token 4.1.1. "iss" (Issuer) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.1):
|
467
447
|
|
468
|
-
> 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
|
448
|
+
> 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.
|
469
449
|
|
470
450
|
You can pass multiple allowed issuers as an Array, verification will pass if one of them matches the `iss` value in the payload.
|
471
451
|
|
@@ -517,7 +497,7 @@ end
|
|
517
497
|
|
518
498
|
From [Oauth JSON Web Token 4.1.3. "aud" (Audience) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.3):
|
519
499
|
|
520
|
-
> The `aud` (audience) claim identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST identify itself with a value in the audience claim. If the principal processing the claim does not identify itself with a value in the `aud` claim when this claim is present, then the JWT MUST be rejected. In the general case, the `aud` value is an array of case-sensitive strings, each containing a
|
500
|
+
> The `aud` (audience) claim identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST identify itself with a value in the audience claim. If the principal processing the claim does not identify itself with a value in the `aud` claim when this claim is present, then the JWT MUST be rejected. In the general case, the `aud` value is an array of case-sensitive strings, each containing a **_StringOrURI_** value. In the special case when the JWT has one audience, the `aud` value MAY be a single case-sensitive string containing a **_StringOrURI_** value. The interpretation of audience values is generally application specific. Use of this claim is OPTIONAL.
|
521
501
|
|
522
502
|
```ruby
|
523
503
|
aud = ['Young', 'Old']
|
@@ -565,9 +545,7 @@ end
|
|
565
545
|
|
566
546
|
From [Oauth JSON Web Token 4.1.6. "iat" (Issued At) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.6):
|
567
547
|
|
568
|
-
> 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
|
569
|
-
|
570
|
-
**Handle Issued At Claim**
|
548
|
+
> 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.
|
571
549
|
|
572
550
|
```ruby
|
573
551
|
iat = Time.now.to_i
|
@@ -587,7 +565,7 @@ end
|
|
587
565
|
|
588
566
|
From [Oauth JSON Web Token 4.1.2. "sub" (Subject) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.2):
|
589
567
|
|
590
|
-
> The `sub` (subject) claim identifies the principal that is the subject of the JWT. The Claims in a JWT are normally statements about the subject. The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique. The processing of this claim is generally application specific. The sub value is a case-sensitive string containing a
|
568
|
+
> The `sub` (subject) claim identifies the principal that is the subject of the JWT. The Claims in a JWT are normally statements about the subject. The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique. The processing of this claim is generally application specific. The sub value is a case-sensitive string containing a **_StringOrURI_** value. Use of this claim is OPTIONAL.
|
591
569
|
|
592
570
|
```ruby
|
593
571
|
sub = 'Subject'
|
@@ -608,6 +586,7 @@ end
|
|
608
586
|
The JWT claim verifications can be used to verify any Hash to include expected keys and values.
|
609
587
|
|
610
588
|
A few example on verifying the claims for a payload:
|
589
|
+
|
611
590
|
```ruby
|
612
591
|
JWT::Claims.verify_payload!({"exp" => Time.now.to_i + 10}, :numeric, :exp)
|
613
592
|
JWT::Claims.valid_payload?({"exp" => Time.now.to_i + 10}, :exp)
|
@@ -644,6 +623,7 @@ end
|
|
644
623
|
### Required Claims
|
645
624
|
|
646
625
|
You can specify claims that must be present for decoding to be successful. JWT::MissingRequiredClaim will be raised if any are missing
|
626
|
+
|
647
627
|
```ruby
|
648
628
|
# Will raise a JWT::MissingRequiredClaim error if the 'exp' claim is absent
|
649
629
|
JWT.decode(token, hmac_secret, true, { required_claims: ['exp'], algorithm: 'HS256' })
|
@@ -782,7 +762,7 @@ jwk_hash = jwk.export
|
|
782
762
|
thumbprint_as_the_kid = jwk_hash[:kid]
|
783
763
|
```
|
784
764
|
|
785
|
-
|
765
|
+
## Development and testing
|
786
766
|
|
787
767
|
The tests are written with rspec. [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
|
788
768
|
|
@@ -791,7 +771,7 @@ bundle install
|
|
791
771
|
bundle exec appraisal rake test
|
792
772
|
```
|
793
773
|
|
794
|
-
|
774
|
+
## Releasing
|
795
775
|
|
796
776
|
To cut a new release adjust the [version.rb](lib/jwt/version.rb) and [CHANGELOG](CHANGELOG.md) with desired version numbers and dates and commit the changes. Tag the release with the version number using the following command:
|
797
777
|
|
@@ -802,6 +782,7 @@ rake release:source_control_push
|
|
802
782
|
This will tag a new version an trigger a [GitHub action](.github/workflows/push_gem.yml) that eventually will push the gem to rubygems.org.
|
803
783
|
|
804
784
|
## How to contribute
|
785
|
+
|
805
786
|
See [CONTRIBUTING](CONTRIBUTING.md).
|
806
787
|
|
807
788
|
## Contributors
|
data/UPGRADING.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Upgrading ruby-jwt to >= 3.0.0
|
2
|
+
|
3
|
+
## Removal of the indirect [RbNaCl](https://github.com/RubyCrypto/rbnacl) dependency
|
4
|
+
|
5
|
+
Historically, the set of supported algorithms was extended by including the `rbnacl` gem in the application's Gemfile. On load, ruby-jwt tried to load the gem and, if available, extend the algorithms to those provided by the `rbnacl/libsodium` libraries. This indirect dependency has caused some maintenance pain and confusion about which versions of the gem are supported.
|
6
|
+
|
7
|
+
Some work to ease the way alternative algorithms can be implemented has been done. This enables the extraction of the algorithm provided by `rbnacl`.
|
8
|
+
|
9
|
+
The extracted algorithms now live in the [jwt-eddsa](https://rubygems.org/gems/jwt-eddsa) gem.
|
10
|
+
|
11
|
+
### Dropped support for HS512256
|
12
|
+
|
13
|
+
The algorithm HS512256 (HMAC-SHA-512 truncated to 256-bits) is not part of any JWA/JWT RFC and therefore will not be supported anymore. It was part of the HMAC algorithms provided by the indirect [RbNaCl](https://github.com/RubyCrypto/rbnacl) dependency. Currently, there are no direct substitutes for the algorithm.
|
14
|
+
|
15
|
+
### `JWT::EncodedToken#payload` will raise before token is verified
|
16
|
+
|
17
|
+
To avoid accidental use of unverified tokens, the `JWT::EncodedToken#payload` method will raise an error if accessed before the token signature has been verified.
|
18
|
+
|
19
|
+
To access the payload before verification, use the method `JWT::EncodedToken#unverified_payload`.
|
20
|
+
|
21
|
+
## Stricter requirements on Base64 encoded data
|
22
|
+
|
23
|
+
Base64 decoding will no longer fallback on the looser RFC 2045. The biggest difference is that the looser version was ignoring whitespaces and newlines, whereas the stricter version raises errors in such cases.
|
24
|
+
|
25
|
+
If you, for example, read tokens from files, there could be problems with trailing newlines. Make sure you trim your input before passing it to the decoding mechanisms.
|
26
|
+
|
27
|
+
## Claim verification revamp
|
28
|
+
|
29
|
+
Claim verification has been [split into separate classes](https://github.com/jwt/ruby-jwt/pull/605) and has [a new API](https://github.com/jwt/ruby-jwt/pull/626), leading to the following deprecations:
|
30
|
+
|
31
|
+
- The `::JWT::ClaimsValidator` class will be removed in favor of the functionality provided by `::JWT::Claims`.
|
32
|
+
- The `::JWT::Claims::verify!` method will be removed in favor of `::JWT::Claims::verify_payload!`.
|
33
|
+
- The `::JWT::JWA.create` method will be removed.
|
34
|
+
- The `::JWT::Verify` class will be removed in favor of the functionality provided by `::JWT::Claims`.
|
35
|
+
- Calling `::JWT::Claims::Numeric.new` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.
|
36
|
+
- Calling `::JWT::Claims::Numeric.verify!` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.
|
37
|
+
|
38
|
+
## Algorithm restructuring
|
39
|
+
|
40
|
+
The internal algorithms were [restructured](https://github.com/jwt/ruby-jwt/pull/607) to support extensions from separate libraries. The changes led to a few deprecations and new requirements:
|
41
|
+
|
42
|
+
- The `sign` and `verify` static methods on all the algorithms (`::JWT::JWA`) will be removed.
|
43
|
+
- Custom algorithms are expected to include the `JWT::JWA::SigningAlgorithm` module.
|
44
|
+
|
45
|
+
## Base64 the `k´ value for HMAC JWKs
|
46
|
+
|
47
|
+
The gem was missing the Base64 encoding and decoding when representing and parsing a HMAC key as a JWK. This issue is now addressed. The added encoding will break compatibility with JWKs produced by older versions of the gem.
|
data/lib/jwt/base64.rb
CHANGED
@@ -14,22 +14,13 @@ module JWT
|
|
14
14
|
end
|
15
15
|
|
16
16
|
# Decode a string with URL-safe Base64 complying with RFC 4648.
|
17
|
-
# Deprecated support for RFC 2045 remains for now. ("All line breaks or other characters not found in Table 1 must be ignored by decoding software")
|
18
17
|
# @api private
|
19
18
|
def url_decode(str)
|
20
19
|
::Base64.urlsafe_decode64(str)
|
21
20
|
rescue ArgumentError => e
|
22
21
|
raise unless e.message == 'invalid base64'
|
23
|
-
raise Base64DecodeError, 'Invalid base64 encoding' if JWT.configuration.strict_base64_decoding
|
24
22
|
|
25
|
-
|
26
|
-
Deprecations.warning('Invalid base64 input detected, could be because of invalid padding, trailing whitespaces or newline chars. Graceful handling of invalid input will be dropped in the next major version of ruby-jwt', only_if_valid: true)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def loose_urlsafe_decode64(str)
|
31
|
-
str += '=' * (4 - str.length.modulo(4))
|
32
|
-
::Base64.decode64(str.tr('-_', '+/'))
|
23
|
+
raise Base64DecodeError, 'Invalid base64 encoding'
|
33
24
|
end
|
34
25
|
end
|
35
26
|
end
|
data/lib/jwt/claims/numeric.rb
CHANGED
@@ -5,18 +5,6 @@ module JWT
|
|
5
5
|
# The Numeric class is responsible for validating numeric claims in a JWT token.
|
6
6
|
# The numeric claims are: exp, iat and nbf
|
7
7
|
class Numeric
|
8
|
-
# The Compat class provides backward compatibility for numeric claim validation.
|
9
|
-
# @api private
|
10
|
-
class Compat
|
11
|
-
def initialize(payload)
|
12
|
-
@payload = payload
|
13
|
-
end
|
14
|
-
|
15
|
-
def verify!
|
16
|
-
JWT::Claims.verify_payload!(@payload, :numeric)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
8
|
# List of numeric claims that can be validated.
|
21
9
|
NUMERIC_CLAIMS = %i[
|
22
10
|
exp
|
@@ -26,14 +14,6 @@ module JWT
|
|
26
14
|
|
27
15
|
private_constant(:NUMERIC_CLAIMS)
|
28
16
|
|
29
|
-
# @api private
|
30
|
-
def self.new(*args)
|
31
|
-
return super if args.empty?
|
32
|
-
|
33
|
-
Deprecations.warning('Calling ::JWT::Claims::Numeric.new with the payload will be removed in the next major version of ruby-jwt')
|
34
|
-
Compat.new(*args)
|
35
|
-
end
|
36
|
-
|
37
17
|
# Verifies the numeric claims in the JWT context.
|
38
18
|
#
|
39
19
|
# @param context [Object] the context containing the JWT payload.
|
@@ -43,18 +23,6 @@ module JWT
|
|
43
23
|
validate_numeric_claims(context.payload)
|
44
24
|
end
|
45
25
|
|
46
|
-
# Verifies the numeric claims in the JWT payload.
|
47
|
-
#
|
48
|
-
# @param payload [Hash] the JWT payload containing the claims.
|
49
|
-
# @param _args [Hash] additional arguments (not used).
|
50
|
-
# @raise [JWT::InvalidClaimError] if any numeric claim is invalid.
|
51
|
-
# @return [nil]
|
52
|
-
# @deprecated The ::JWT::Claims::Numeric.verify! method will be removed in the next major version of ruby-jwt
|
53
|
-
def self.verify!(payload:, **_args)
|
54
|
-
Deprecations.warning('The ::JWT::Claims::Numeric.verify! method will be removed in the next major version of ruby-jwt.')
|
55
|
-
JWT::Claims.verify_payload!(payload, :numeric)
|
56
|
-
end
|
57
|
-
|
58
26
|
private
|
59
27
|
|
60
28
|
def validate_numeric_claims(payload)
|
data/lib/jwt/claims.rb
CHANGED
@@ -11,7 +11,6 @@ require_relative 'claims/not_before'
|
|
11
11
|
require_relative 'claims/numeric'
|
12
12
|
require_relative 'claims/required'
|
13
13
|
require_relative 'claims/subject'
|
14
|
-
require_relative 'claims/verification_methods'
|
15
14
|
require_relative 'claims/verifier'
|
16
15
|
|
17
16
|
module JWT
|
@@ -33,12 +32,6 @@ module JWT
|
|
33
32
|
Error = Struct.new(:message, keyword_init: true)
|
34
33
|
|
35
34
|
class << self
|
36
|
-
# @deprecated Use {verify_payload!} instead. Will be removed in the next major version of ruby-jwt.
|
37
|
-
def verify!(payload, options)
|
38
|
-
Deprecations.warning('The ::JWT::Claims.verify! method is deprecated will be removed in the next major version of ruby-jwt')
|
39
|
-
DecodeVerifier.verify!(payload, options)
|
40
|
-
end
|
41
|
-
|
42
35
|
# Checks if the claims in the JWT payload are valid.
|
43
36
|
# @example
|
44
37
|
#
|