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.
data/README.md CHANGED
@@ -2,9 +2,8 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/jwt.svg)](https://badge.fury.io/rb/jwt)
4
4
  [![Build Status](https://github.com/jwt/ruby-jwt/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/jwt/ruby-jwt/actions)
5
- [![Code Climate](https://codeclimate.com/github/jwt/ruby-jwt/badges/gpa.svg)](https://codeclimate.com/github/jwt/ruby-jwt)
6
- [![Test Coverage](https://codeclimate.com/github/jwt/ruby-jwt/badges/coverage.svg)](https://codeclimate.com/github/jwt/ruby-jwt/coverage)
7
- [![Issue Count](https://codeclimate.com/github/jwt/ruby-jwt/badges/issue_count.svg)](https://codeclimate.com/github/jwt/ruby-jwt)
5
+ [![Maintainability](https://qlty.sh/badges/6f61c5a6-6e23-41a7-8896-a3ce8b006655/maintainability.svg)](https://qlty.sh/gh/jwt/projects/ruby-jwt)
6
+ [![Code Coverage](https://qlty.sh/badges/6f61c5a6-6e23-41a7-8896-a3ce8b006655/test_coverage.svg)](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
- Notable changes in the upcoming **version 3.0**:
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
- |![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)|
20
+ | Logo | Message |
21
+ | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
22
+ | ![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) |
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 [separate gem](https://rubygems.org/gems/jwt-eddsa).
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: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1)
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
- * none - unsigned token
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
- * HS256 - HMAC using SHA-256 hash algorithm
120
- * HS384 - HMAC using SHA-384 hash algorithm
121
- * HS512 - HMAC using SHA-512 hash algorithm
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
- * RS256 - RSA using SHA-256 hash algorithm
145
- * RS384 - RSA using SHA-384 hash algorithm
146
- * RS512 - RSA using SHA-512 hash algorithm
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
- * ES256 - ECDSA using P-256 and SHA-256
170
- * ES384 - ECDSA using P-384 and SHA-384
171
- * ES512 - ECDSA using P-521 and SHA-512
172
- * ES256K - ECDSA using P-256K and SHA-256
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
- ### **EDDSA**
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
- In order to use this algorithm you need to add the `openssl` gem to your `Gemfile` with a version greater or equal to `2.1`.
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
- gem 'openssl', '~> 2.1'
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
- The `JWT::EncodedToken` can be used to create a token object that allows verification of signatures and claims
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
- - 'exp' (Expiration Time) Claim
365
- - 'nbf' (Not Before Time) Claim
366
- - 'iss' (Issuer) Claim
367
- - 'aud' (Audience) Claim
368
- - 'jti' (JWT ID) Claim
369
- - 'iat' (Issued At) Claim
370
- - 'sub' (Subject) Claim
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 ***NumericDate*** value. Use of this claim is OPTIONAL.
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
- **Adding Leeway**
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 ***NumericDate*** value. Use of this claim is OPTIONAL.
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
- **Adding Leeway**
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 ***StringOrURI*** value. Use of this claim is OPTIONAL.
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 ***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.
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 ***NumericDate*** value. Use of this claim is OPTIONAL.
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 ***StringOrURI*** value. Use of this claim is OPTIONAL.
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
- # Development and testing
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
- # Releasing
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
- loose_urlsafe_decode64(str).tap do
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
@@ -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
  #
@@ -30,7 +30,6 @@ module JWT
30
30
  def reset!
31
31
  @decode = DecodeConfiguration.new
32
32
  @jwk = JwkConfiguration.new
33
- @strict_base64_decoding = false
34
33
 
35
34
  self.deprecation_warnings = :once
36
35
  end