jwt 2.7.1 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -27
  3. data/README.md +37 -19
  4. data/lib/jwt/base64.rb +16 -2
  5. data/lib/jwt/claims/audience.rb +20 -0
  6. data/lib/jwt/claims/expiration.rb +22 -0
  7. data/lib/jwt/claims/issued_at.rb +15 -0
  8. data/lib/jwt/claims/issuer.rb +24 -0
  9. data/lib/jwt/claims/jwt_id.rb +25 -0
  10. data/lib/jwt/claims/not_before.rb +22 -0
  11. data/lib/jwt/claims/numeric.rb +43 -0
  12. data/lib/jwt/claims/required.rb +23 -0
  13. data/lib/jwt/claims/subject.rb +20 -0
  14. data/lib/jwt/claims.rb +38 -0
  15. data/lib/jwt/configuration/container.rb +14 -3
  16. data/lib/jwt/configuration/jwk_configuration.rb +1 -1
  17. data/lib/jwt/decode.rb +12 -21
  18. data/lib/jwt/deprecations.rb +48 -0
  19. data/lib/jwt/encode.rb +4 -14
  20. data/lib/jwt/error.rb +1 -0
  21. data/lib/jwt/{algos → jwa}/ecdsa.rb +39 -26
  22. data/lib/jwt/jwa/eddsa.rb +34 -0
  23. data/lib/jwt/{algos → jwa}/hmac.rb +25 -19
  24. data/lib/jwt/jwa/hmac_rbnacl.rb +45 -0
  25. data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +42 -0
  26. data/lib/jwt/jwa/none.rb +23 -0
  27. data/lib/jwt/jwa/ps.rb +36 -0
  28. data/lib/jwt/jwa/rsa.rb +36 -0
  29. data/lib/jwt/jwa/signing_algorithm.rb +59 -0
  30. data/lib/jwt/jwa/unsupported.rb +19 -0
  31. data/lib/jwt/jwa/wrapper.rb +43 -0
  32. data/lib/jwt/jwa.rb +45 -0
  33. data/lib/jwt/jwk/ec.rb +42 -27
  34. data/lib/jwt/jwk/key_base.rb +3 -1
  35. data/lib/jwt/jwk/key_finder.rb +4 -4
  36. data/lib/jwt/jwk/set.rb +1 -1
  37. data/lib/jwt/jwk.rb +1 -1
  38. data/lib/jwt/version.rb +4 -3
  39. data/lib/jwt/x5c_key_finder.rb +2 -5
  40. data/lib/jwt.rb +5 -1
  41. data/ruby-jwt.gemspec +3 -0
  42. metadata +58 -20
  43. data/lib/jwt/algos/algo_wrapper.rb +0 -26
  44. data/lib/jwt/algos/eddsa.rb +0 -33
  45. data/lib/jwt/algos/hmac_rbnacl.rb +0 -53
  46. data/lib/jwt/algos/hmac_rbnacl_fixed.rb +0 -52
  47. data/lib/jwt/algos/none.rb +0 -19
  48. data/lib/jwt/algos/ps.rb +0 -41
  49. data/lib/jwt/algos/rsa.rb +0 -23
  50. data/lib/jwt/algos/unsupported.rb +0 -19
  51. data/lib/jwt/algos.rb +0 -66
  52. data/lib/jwt/claims_validator.rb +0 -37
  53. data/lib/jwt/verify.rb +0 -113
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11007e8ec36d148026cd5b6761681b0a71437b7b461efba4ae492622fc5ff27b
4
- data.tar.gz: 8090bbba3dce57e42cc203ef168d3d00c624e79e076de0e949b4390b531b4d55
3
+ metadata.gz: 0f3d3ea752a6b4dee8f1b020a3e0af95e003f74a8c40f730faab73b616fc9e98
4
+ data.tar.gz: 8dcdb470771f56931485824c32739bfcec1ce310b7096d6cd28c656d0f8c21fd
5
5
  SHA512:
6
- metadata.gz: a035f44be760ad325105329cbaec12e90515afdade9649e798ee5c7cefced3271d4f3084afeef693c03c961541d9e5e45b2876734d1947dccdd24cc194acbca2
7
- data.tar.gz: 61e3d071ce44809767f3501f12b452cae574f9ea0de668ca937731838d58e2a9fa85831829ce564f6a015177a86b2a05b1b6ddf60ba34ac515ea12c69ac618fe
6
+ metadata.gz: c68cccb66154a824751df6e7cd4d0b9d31372c0bada498097770feb8992ac3c7a462cd11eb759171d096e8d957b2a67b088fb35dc79fe7b4dd6eac6b25140d63
7
+ data.tar.gz: 03fe234584cce6458fb1a0f64cb399f81bc3c7aee40e25c011f0c27484ede3c6c6a6d950a9c5f3f13e4d66db5a76de98f2801942762df514097b2f663bf79247
data/CHANGELOG.md CHANGED
@@ -1,13 +1,70 @@
1
1
  # Changelog
2
2
 
3
+ ## [v2.9.0](https://github.com/jwt/ruby-jwt/tree/v2.9.0) (NEXT)
4
+
5
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.8.2...v2.9.0)
6
+
7
+ **Features:**
8
+
9
+ - Build and push gem using a GH action [#612](https://github.com/jwt/ruby-jwt/pull/612) ([@anakinj](https://github.com/anakinj))
10
+
11
+ **Fixes and enhancements:**
12
+
13
+ - Refactor claim validators into their own classes [#605](https://github.com/jwt/ruby-jwt/pull/605) ([@anakinj](https://github.com/anakinj), [@MatteoPierro](https://github.com/MatteoPierro))
14
+ - Allow extending available algorithms [#607](https://github.com/jwt/ruby-jwt/pull/607) ([@anakinj](https://github.com/anakinj))
15
+ - Do not include the EdDSA algorithm if rbnacl not available [#613](https://github.com/jwt/ruby-jwt/pull/613) ([@anakinj](https://github.com/anakinj))
16
+
17
+ ## [v2.8.2](https://github.com/jwt/ruby-jwt/tree/v2.8.2) (2024-06-18)
18
+
19
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.8.1...v2.8.2)
20
+
21
+ **Fixes and enhancements:**
22
+
23
+ - Print deprecation warnings only on when token decoding succeeds [#600](https://github.com/jwt/ruby-jwt/pull/600) ([@anakinj](https://github.com/anakinj))
24
+ - Unify code style [#602](https://github.com/jwt/ruby-jwt/pull/602) ([@anakinj](https://github.com/anakinj))
25
+
26
+ ## [v2.8.1](https://github.com/jwt/ruby-jwt/tree/v2.8.1) (2024-02-29)
27
+
28
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.8.0...v2.8.1)
29
+
30
+ **Features:**
31
+
32
+ - Configurable base64 decode behaviour [#589](https://github.com/jwt/ruby-jwt/pull/589) ([@anakinj](https://github.com/anakinj))
33
+
34
+ **Fixes and enhancements:**
35
+
36
+ - Output deprecation warnings once [#589](https://github.com/jwt/ruby-jwt/pull/589) ([@anakinj](https://github.com/anakinj))
37
+
38
+ ## [v2.8.0](https://github.com/jwt/ruby-jwt/tree/v2.8.0) (2024-02-17)
39
+
40
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.7.1...v2.8.0)
41
+
42
+ **Features:**
43
+
44
+ - Updated rubocop to 1.56 [#573](https://github.com/jwt/ruby-jwt/pull/573) ([@anakinj](https://github.com/anakinj))
45
+ - Run CI on Ruby 3.3 [#577](https://github.com/jwt/ruby-jwt/pull/577) ([@anakinj](https://github.com/anakinj))
46
+ - Deprecation warning added for the HMAC algorithm HS512256 (HMAC-SHA-512 truncated to 256-bits) [#575](https://github.com/jwt/ruby-jwt/pull/575) ([@anakinj](https://github.com/anakinj))
47
+ - Stop using RbNaCl for standard HMAC algorithms [#575](https://github.com/jwt/ruby-jwt/pull/575) ([@anakinj](https://github.com/anakinj))
48
+
49
+ **Fixes and enhancements:**
50
+
51
+ - Fix signature has expired error if payload is a string [#555](https://github.com/jwt/ruby-jwt/pull/555) ([@GobinathAL](https://github.com/GobinathAL))
52
+ - Fix key base equality and spaceship operators [#569](https://github.com/jwt/ruby-jwt/pull/569) ([@magneland](https://github.com/magneland))
53
+ - Remove explicit base64 require from x5c_key_finder [#580](https://github.com/jwt/ruby-jwt/pull/580) ([@anakinj](https://github.com/anakinj))
54
+ - Performance improvements and cleanup of tests [#581](https://github.com/jwt/ruby-jwt/pull/581) ([@anakinj](https://github.com/anakinj))
55
+ - Repair EC x/y coordinates when importing JWK [#585](https://github.com/jwt/ruby-jwt/pull/585) ([@julik](https://github.com/julik))
56
+ - Explicit dependency to the base64 gem [#582](https://github.com/jwt/ruby-jwt/pull/582) ([@anakinj](https://github.com/anakinj))
57
+ - Deprecation warning for decoding content not compliant with RFC 4648 [#582](https://github.com/jwt/ruby-jwt/pull/582) ([@anakinj](https://github.com/anakinj))
58
+ - Algorithms moved under the `::JWT::JWA` module ([@anakinj](https://github.com/anakinj))
59
+
3
60
  ## [v2.7.1](https://github.com/jwt/ruby-jwt/tree/v2.8.0) (2023-06-09)
4
61
 
5
- [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.7.0...v2.8.0)
62
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.7.0...v2.7.1)
6
63
 
7
64
  **Fixes and enhancements:**
8
65
 
9
- - Handle invalid algorithm when decoding JWT [#559](https://github.com/jwt/ruby-jwt/pull/559) - [@nataliastanko](https://github.com/nataliastanko)
10
- - Do not raise error when verifying bad HMAC signature [#563](https://github.com/jwt/ruby-jwt/pull/563) - [@hieuk09](https://github.com/hieuk09)
66
+ - Handle invalid algorithm when decoding JWT [#559](https://github.com/jwt/ruby-jwt/pull/559) ([@nataliastanko](https://github.com/nataliastanko))
67
+ - Do not raise error when verifying bad HMAC signature [#563](https://github.com/jwt/ruby-jwt/pull/563) ([@hieuk09](https://github.com/hieuk09))
11
68
 
12
69
  ## [v2.7.0](https://github.com/jwt/ruby-jwt/tree/v2.7.0) (2023-02-01)
13
70
 
@@ -29,14 +86,14 @@
29
86
 
30
87
  **Features:**
31
88
 
32
- - Support custom algorithms by passing algorithm objects[#512](https://github.com/jwt/ruby-jwt/pull/512) ([@anakinj](https://github.com/anakinj)).
33
- - Support descriptive (not key related) JWK parameters[#520](https://github.com/jwt/ruby-jwt/pull/520) ([@bellebaum](https://github.com/bellebaum)).
34
- - Support for JSON Web Key Sets[#525](https://github.com/jwt/ruby-jwt/pull/525) ([@bellebaum](https://github.com/bellebaum)).
35
- - Support HMAC keys over 32 chars when using RbNaCl[#521](https://github.com/jwt/ruby-jwt/pull/521) ([@anakinj](https://github.com/anakinj)).
89
+ - Support custom algorithms by passing algorithm objects [#512](https://github.com/jwt/ruby-jwt/pull/512) ([@anakinj](https://github.com/anakinj))
90
+ - Support descriptive (not key related) JWK parameters [#520](https://github.com/jwt/ruby-jwt/pull/520) ([@bellebaum](https://github.com/bellebaum))
91
+ - Support for JSON Web Key Sets [#525](https://github.com/jwt/ruby-jwt/pull/525) ([@bellebaum](https://github.com/bellebaum))
92
+ - Support HMAC keys over 32 chars when using RbNaCl [#521](https://github.com/jwt/ruby-jwt/pull/521) ([@anakinj](https://github.com/anakinj))
36
93
 
37
94
  **Fixes and enhancements:**
38
95
 
39
- - Raise descriptive error on empty hmac_secret and OpenSSL 3.0/openssl gem <3.0.1 [#530](https://github.com/jwt/ruby-jwt/pull/530) ([@jonmchan](https://github.com/jonmchan)).
96
+ - Raise descriptive error on empty hmac_secret and OpenSSL 3.0/openssl gem <3.0.1 [#530](https://github.com/jwt/ruby-jwt/pull/530) ([@jonmchan](https://github.com/jonmchan))
40
97
 
41
98
  ## [v2.5.0](https://github.com/jwt/ruby-jwt/tree/v2.5.0) (2022-08-25)
42
99
 
@@ -44,21 +101,21 @@
44
101
 
45
102
  **Features:**
46
103
 
47
- - Support JWK thumbprints as key ids [#481](https://github.com/jwt/ruby-jwt/pull/481) ([@anakinj](https://github.com/anakinj)).
48
- - Support OpenSSL >= 3.0 [#496](https://github.com/jwt/ruby-jwt/pull/496) ([@anakinj](https://github.com/anakinj)).
104
+ - Support JWK thumbprints as key ids [#481](https://github.com/jwt/ruby-jwt/pull/481) ([@anakinj](https://github.com/anakinj))
105
+ - Support OpenSSL >= 3.0 [#496](https://github.com/jwt/ruby-jwt/pull/496) ([@anakinj](https://github.com/anakinj))
49
106
 
50
107
  **Fixes and enhancements:**
51
- - Bring back the old Base64 (RFC2045) deocode mechanisms [#488](https://github.com/jwt/ruby-jwt/pull/488) ([@anakinj](https://github.com/anakinj)).
52
- - Rescue RbNaCl exception for EdDSA wrong key [#491](https://github.com/jwt/ruby-jwt/pull/491) ([@n-studio](https://github.com/n-studio)).
53
- - New parameter name for cases when kid is not found using JWK key loader proc [#501](https://github.com/jwt/ruby-jwt/pull/501) ([@anakinj](https://github.com/anakinj)).
54
- - Fix NoMethodError when a 2 segment token is missing 'alg' header [#502](https://github.com/jwt/ruby-jwt/pull/502) ([@cmrd-senya](https://github.com/cmrd-senya)).
108
+ - Bring back the old Base64 (RFC2045) deocode mechanisms [#488](https://github.com/jwt/ruby-jwt/pull/488) ([@anakinj](https://github.com/anakinj))
109
+ - Rescue RbNaCl exception for EdDSA wrong key [#491](https://github.com/jwt/ruby-jwt/pull/491) ([@n-studio](https://github.com/n-studio))
110
+ - New parameter name for cases when kid is not found using JWK key loader proc [#501](https://github.com/jwt/ruby-jwt/pull/501) ([@anakinj](https://github.com/anakinj))
111
+ - Fix NoMethodError when a 2 segment token is missing 'alg' header [#502](https://github.com/jwt/ruby-jwt/pull/502) ([@cmrd-senya](https://github.com/cmrd-senya))
55
112
 
56
113
  ## [v2.4.1](https://github.com/jwt/ruby-jwt/tree/v2.4.1) (2022-06-07)
57
114
 
58
115
  [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.4.0...v2.4.1)
59
116
 
60
117
  **Fixes and enhancements:**
61
- - Raise JWT::DecodeError on invalid signature [\#484](https://github.com/jwt/ruby-jwt/pull/484) ([@freakyfelt!](https://github.com/freakyfelt!)).
118
+ - Raise JWT::DecodeError on invalid signature [\#484](https://github.com/jwt/ruby-jwt/pull/484) ([@freakyfelt!](https://github.com/freakyfelt!))
62
119
 
63
120
  ## [v2.4.0](https://github.com/jwt/ruby-jwt/tree/v2.4.0) (2022-06-06)
64
121
 
@@ -66,20 +123,20 @@
66
123
 
67
124
  **Features:**
68
125
 
69
- - Dropped support for Ruby 2.5 and older [#453](https://github.com/jwt/ruby-jwt/pull/453) - [@anakinj](https://github.com/anakinj).
70
- - Use Ruby built-in url-safe base64 methods [#454](https://github.com/jwt/ruby-jwt/pull/454) - [@bdewater](https://github.com/bdewater).
71
- - Updated rubocop to 1.23.0 [#457](https://github.com/jwt/ruby-jwt/pull/457) - [@anakinj](https://github.com/anakinj).
72
- - Add x5c header key finder [#338](https://github.com/jwt/ruby-jwt/pull/338) - [@bdewater](https://github.com/bdewater).
73
- - Author driven changelog process [#463](https://github.com/jwt/ruby-jwt/pull/463) - [@anakinj](https://github.com/anakinj).
74
- - Allow regular expressions and procs to verify issuer [\#437](https://github.com/jwt/ruby-jwt/pull/437) ([rewritten](https://github.com/rewritten)).
75
- - Add Support to be able to verify from multiple keys [\#425](https://github.com/jwt/ruby-jwt/pull/425) ([ritikesh](https://github.com/ritikesh)).
126
+ - Dropped support for Ruby 2.5 and older [#453](https://github.com/jwt/ruby-jwt/pull/453) - ([@anakinj](https://github.com/anakinj))
127
+ - Use Ruby built-in url-safe base64 methods [#454](https://github.com/jwt/ruby-jwt/pull/454) - ([@bdewater](https://github.com/bdewater))
128
+ - Updated rubocop to 1.23.0 [#457](https://github.com/jwt/ruby-jwt/pull/457) - ([@anakinj](https://github.com/anakinj))
129
+ - Add x5c header key finder [#338](https://github.com/jwt/ruby-jwt/pull/338) - ([@bdewater](https://github.com/bdewater))
130
+ - Author driven changelog process [#463](https://github.com/jwt/ruby-jwt/pull/463) - ([@anakinj](https://github.com/anakinj))
131
+ - Allow regular expressions and procs to verify issuer [\#437](https://github.com/jwt/ruby-jwt/pull/437) ([rewritten](https://github.com/rewritten))
132
+ - Add Support to be able to verify from multiple keys [\#425](https://github.com/jwt/ruby-jwt/pull/425) ([ritikesh](https://github.com/ritikesh))
76
133
 
77
134
  **Fixes and enhancements:**
78
- - Readme: Typo fix re MissingRequiredClaim [\#451](https://github.com/jwt/ruby-jwt/pull/451) ([antonmorant](https://github.com/antonmorant)).
79
- - Fix RuboCop TODOs [\#476](https://github.com/jwt/ruby-jwt/pull/476) ([typhoon2099](https://github.com/typhoon2099)).
80
- - Make specific algorithms in README linkable [\#472](https://github.com/jwt/ruby-jwt/pull/472) ([milieu](https://github.com/milieu)).
81
- - Update note about supported JWK types [\#475](https://github.com/jwt/ruby-jwt/pull/475) ([dpashkevich](https://github.com/dpashkevich)).
82
- - Create CODE\_OF\_CONDUCT.md [\#449](https://github.com/jwt/ruby-jwt/pull/449) ([loic5](https://github.com/loic5)).
135
+ - Readme: Typo fix re MissingRequiredClaim [\#451](https://github.com/jwt/ruby-jwt/pull/451) ([antonmorant](https://github.com/antonmorant))
136
+ - Fix RuboCop TODOs [\#476](https://github.com/jwt/ruby-jwt/pull/476) ([typhoon2099](https://github.com/typhoon2099))
137
+ - Make specific algorithms in README linkable [\#472](https://github.com/jwt/ruby-jwt/pull/472) ([milieu](https://github.com/milieu))
138
+ - Update note about supported JWK types [\#475](https://github.com/jwt/ruby-jwt/pull/475) ([dpashkevich](https://github.com/dpashkevich))
139
+ - Create CODE\_OF\_CONDUCT.md [\#449](https://github.com/jwt/ruby-jwt/pull/449) ([loic5](https://github.com/loic5))
83
140
 
84
141
  ## [v2.3.0](https://github.com/jwt/ruby-jwt/tree/v2.3.0) (2021-10-03)
85
142
 
data/README.md CHANGED
@@ -43,6 +43,23 @@ The JWT spec supports NONE, HMAC, RSASSA, ECDSA and RSASSA-PSS algorithms for cr
43
43
 
44
44
  See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1)
45
45
 
46
+ ### Deprecation warnings
47
+
48
+ 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.
49
+
50
+ ```ruby
51
+ JWT.configuration.deprecation_warnings = :warn # default is :once
52
+ ```
53
+
54
+ ### Base64 decoding
55
+
56
+ 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).
57
+
58
+ The stricter base64 decoding when processing tokens can be done via the `strict_base64_decoding` configuration accessor.
59
+ ```ruby
60
+ JWT.configuration.strict_base64_decoding = true # default is false
61
+ ```
62
+
46
63
  ### **NONE**
47
64
 
48
65
  * none - unsigned token
@@ -72,7 +89,6 @@ puts decoded_token
72
89
  ### **HMAC**
73
90
 
74
91
  * HS256 - HMAC using SHA-256 hash algorithm
75
- * HS512256 - HMAC using SHA-512-256 hash algorithm (only available with RbNaCl; see note below)
76
92
  * HS384 - HMAC using SHA-384 hash algorithm
77
93
  * HS512 - HMAC using SHA-512 hash algorithm
78
94
 
@@ -95,12 +111,6 @@ decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
95
111
  puts decoded_token
96
112
  ```
97
113
 
98
- Note: If [RbNaCl](https://github.com/RubyCrypto/rbnacl) is loadable, ruby-jwt will use it for HMAC-SHA256, HMAC-SHA512-256, and HMAC-SHA512. RbNaCl prior to 6.0.0 only support a maximum key size of 32 bytes for these algorithms.
99
-
100
- [RbNaCl](https://github.com/RubyCrypto/rbnacl) requires
101
- [libsodium](https://github.com/jedisct1/libsodium), it can be installed
102
- on MacOS with `brew install libsodium`.
103
-
104
114
  ### **RSA**
105
115
 
106
116
  * RS256 - RSA using SHA-256 hash algorithm
@@ -213,18 +223,22 @@ puts decoded_token
213
223
 
214
224
  ### **Custom algorithms**
215
225
 
216
- An object implementing custom signing or verification behaviour can be passed in the `algorithm` option when encoding and decoding. The given object needs to implement the method `valid_alg?` and `verify` and/or `alg` and `sign`, depending if object is used for encoding or decoding.
226
+ 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:
227
+
228
+ - For decoding/verifying: The object must implement the methods `alg` and `verify`.
229
+ - For encoding/signing: The object must implement the methods `alg` and `sign`.
230
+
231
+ For customization options check the details from `JWT::JWA::SigningAlgorithm`.
232
+
217
233
 
218
234
  ```ruby
219
235
  module CustomHS512Algorithm
236
+ extend JWT::JWA::SigningAlgorithm
237
+
220
238
  def self.alg
221
239
  'HS512'
222
240
  end
223
241
 
224
- def self.valid_alg?(alg_to_validate)
225
- alg_to_validate == alg
226
- end
227
-
228
242
  def self.sign(data:, signing_key:)
229
243
  OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha512'), data, signing_key)
230
244
  end
@@ -561,7 +575,7 @@ crls = crl_uris.map do |uri|
561
575
  end
562
576
 
563
577
  begin
564
- JWT.decode(token, nil, true, { x5c: { root_certificates: root_certificates, crls: crls })
578
+ JWT.decode(token, nil, true, { x5c: { root_certificates: root_certificates, crls: crls } })
565
579
  rescue JWT::DecodeError
566
580
  # Handle error, e.g. x5c header certificate revoked or expired
567
581
  end
@@ -680,21 +694,25 @@ jwk_hash = jwk.export
680
694
  thumbprint_as_the_kid = jwk_hash[:kid]
681
695
  ```
682
696
 
683
- # Development and Tests
697
+ # Development and testing
684
698
 
685
- We depend on [Bundler](http://rubygems.org/gems/bundler) for defining gemspec and performing releases to rubygems.org, which can be done with
699
+ The tests are written with rspec. [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
686
700
 
687
701
  ```bash
688
- rake release
702
+ bundle install
703
+ bundle exec appraisal rake test
689
704
  ```
690
705
 
691
- The tests are written with rspec. [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
706
+ # Releasing
707
+
708
+ 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:
692
709
 
693
710
  ```bash
694
- bundle install
695
- bundle exec appraisal rake test
711
+ rake release:source_control_push
696
712
  ```
697
713
 
714
+ 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.
715
+
698
716
  ## How to contribute
699
717
  See [CONTRIBUTING](CONTRIBUTING.md).
700
718
 
data/lib/jwt/base64.rb CHANGED
@@ -3,14 +3,28 @@
3
3
  require 'base64'
4
4
 
5
5
  module JWT
6
- # Base64 helpers
6
+ # Base64 encoding and decoding
7
7
  class Base64
8
8
  class << self
9
+ # Encode a string with URL-safe Base64 complying with RFC 4648 (not padded).
9
10
  def url_encode(str)
10
- ::Base64.encode64(str).tr('+/', '-_').gsub(/[\n=]/, '')
11
+ ::Base64.urlsafe_encode64(str, padding: false)
11
12
  end
12
13
 
14
+ # Decode a string with URL-safe Base64 complying with RFC 4648.
15
+ # 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")
13
16
  def url_decode(str)
17
+ ::Base64.urlsafe_decode64(str)
18
+ rescue ArgumentError => e
19
+ raise unless e.message == 'invalid base64'
20
+ raise Base64DecodeError, 'Invalid base64 encoding' if JWT.configuration.strict_base64_decoding
21
+
22
+ loose_urlsafe_decode64(str).tap do
23
+ 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)
24
+ end
25
+ end
26
+
27
+ def loose_urlsafe_decode64(str)
14
28
  str += '=' * (4 - str.length.modulo(4))
15
29
  ::Base64.decode64(str.tr('-_', '+/'))
16
30
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Audience
6
+ def initialize(expected_audience:)
7
+ @expected_audience = expected_audience
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ aud = context.payload['aud']
12
+ raise JWT::InvalidAudError, "Invalid audience. Expected #{expected_audience}, received #{aud || '<none>'}" if ([*aud] & [*expected_audience]).empty?
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :expected_audience
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Expiration
6
+ def initialize(leeway:)
7
+ @leeway = leeway || 0
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ return unless context.payload.is_a?(Hash)
12
+ return unless context.payload.key?('exp')
13
+
14
+ raise JWT::ExpiredSignature, 'Signature has expired' if context.payload['exp'].to_i <= (Time.now.to_i - leeway)
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :leeway
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class IssuedAt
6
+ def verify!(context:, **_args)
7
+ return unless context.payload.is_a?(Hash)
8
+ return unless context.payload.key?('iat')
9
+
10
+ iat = context.payload['iat']
11
+ raise(JWT::InvalidIatError, 'Invalid iat') if !iat.is_a?(::Numeric) || iat.to_f > Time.now.to_f
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Issuer
6
+ def initialize(issuers:)
7
+ @issuers = Array(issuers).map { |item| item.is_a?(Symbol) ? item.to_s : item }
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ case (iss = context.payload['iss'])
12
+ when *issuers
13
+ nil
14
+ else
15
+ raise JWT::InvalidIssuerError, "Invalid issuer. Expected #{issuers}, received #{iss || '<none>'}"
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :issuers
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class JwtId
6
+ def initialize(validator:)
7
+ @validator = validator
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ jti = context.payload['jti']
12
+ if validator.respond_to?(:call)
13
+ verified = validator.arity == 2 ? validator.call(jti, context.payload) : validator.call(jti)
14
+ raise(JWT::InvalidJtiError, 'Invalid jti') unless verified
15
+ elsif jti.to_s.strip.empty?
16
+ raise(JWT::InvalidJtiError, 'Missing jti')
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :validator
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class NotBefore
6
+ def initialize(leeway:)
7
+ @leeway = leeway || 0
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ return unless context.payload.is_a?(Hash)
12
+ return unless context.payload.key?('nbf')
13
+
14
+ raise JWT::ImmatureSignature, 'Signature nbf has not been reached' if context.payload['nbf'].to_i > (Time.now.to_i + leeway)
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :leeway
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Numeric
6
+ def self.verify!(payload:, **_args)
7
+ return unless payload.is_a?(Hash)
8
+
9
+ new(payload).verify!
10
+ end
11
+
12
+ NUMERIC_CLAIMS = %i[
13
+ exp
14
+ iat
15
+ nbf
16
+ ].freeze
17
+
18
+ def initialize(payload)
19
+ @payload = payload.transform_keys(&:to_sym)
20
+ end
21
+
22
+ def verify!
23
+ validate_numeric_claims
24
+
25
+ true
26
+ end
27
+
28
+ private
29
+
30
+ def validate_numeric_claims
31
+ NUMERIC_CLAIMS.each do |claim|
32
+ validate_is_numeric(claim) if @payload.key?(claim)
33
+ end
34
+ end
35
+
36
+ def validate_is_numeric(claim)
37
+ return if @payload[claim].is_a?(::Numeric)
38
+
39
+ raise InvalidPayload, "#{claim} claim must be a Numeric value but it is a #{@payload[claim].class}"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Required
6
+ def initialize(required_claims:)
7
+ @required_claims = required_claims
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ required_claims.each do |required_claim|
12
+ next if context.payload.is_a?(Hash) && context.payload.key?(required_claim)
13
+
14
+ raise JWT::MissingRequiredClaim, "Missing required claim #{required_claim}"
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :required_claims
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Claims
5
+ class Subject
6
+ def initialize(expected_subject:)
7
+ @expected_subject = expected_subject.to_s
8
+ end
9
+
10
+ def verify!(context:, **_args)
11
+ sub = context.payload['sub']
12
+ raise(JWT::InvalidSubError, "Invalid subject. Expected #{expected_subject}, received #{sub || '<none>'}") unless sub.to_s == expected_subject
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :expected_subject
18
+ end
19
+ end
20
+ end
data/lib/jwt/claims.rb ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'claims/audience'
4
+ require_relative 'claims/expiration'
5
+ require_relative 'claims/issued_at'
6
+ require_relative 'claims/issuer'
7
+ require_relative 'claims/jwt_id'
8
+ require_relative 'claims/not_before'
9
+ require_relative 'claims/numeric'
10
+ require_relative 'claims/required'
11
+ require_relative 'claims/subject'
12
+
13
+ module JWT
14
+ module Claims
15
+ VerificationContext = Struct.new(:payload, keyword_init: true)
16
+
17
+ VERIFIERS = {
18
+ verify_expiration: ->(options) { Claims::Expiration.new(leeway: options[:exp_leeway] || options[:leeway]) },
19
+ verify_not_before: ->(options) { Claims::NotBefore.new(leeway: options[:nbf_leeway] || options[:leeway]) },
20
+ verify_iss: ->(options) { Claims::Issuer.new(issuers: options[:iss]) },
21
+ verify_iat: ->(*) { Claims::IssuedAt.new },
22
+ verify_jti: ->(options) { Claims::JwtId.new(validator: options[:verify_jti]) },
23
+ verify_aud: ->(options) { Claims::Audience.new(expected_audience: options[:aud]) },
24
+ verify_sub: ->(options) { options[:sub] && Claims::Subject.new(expected_subject: options[:sub]) },
25
+ required_claims: ->(options) { Claims::Required.new(required_claims: options[:required_claims]) }
26
+ }.freeze
27
+
28
+ class << self
29
+ def verify!(payload, options)
30
+ VERIFIERS.each do |key, verifier_builder|
31
+ next unless options[key]
32
+
33
+ verifier_builder&.call(options)&.verify!(context: VerificationContext.new(payload: payload))
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -6,15 +6,26 @@ require_relative 'jwk_configuration'
6
6
  module JWT
7
7
  module Configuration
8
8
  class Container
9
- attr_accessor :decode, :jwk
9
+ attr_accessor :decode, :jwk, :strict_base64_decoding
10
+ attr_reader :deprecation_warnings
10
11
 
11
12
  def initialize
12
13
  reset!
13
14
  end
14
15
 
15
16
  def reset!
16
- @decode = DecodeConfiguration.new
17
- @jwk = JwkConfiguration.new
17
+ @decode = DecodeConfiguration.new
18
+ @jwk = JwkConfiguration.new
19
+ @strict_base64_decoding = false
20
+
21
+ self.deprecation_warnings = :once
22
+ end
23
+
24
+ DEPRECATION_WARNINGS_VALUES = %i[once warn silent].freeze
25
+ def deprecation_warnings=(value)
26
+ raise ArgumentError, "Invalid deprecation_warnings value #{value}. Supported values: #{DEPRECATION_WARNINGS_VALUES}" unless DEPRECATION_WARNINGS_VALUES.include?(value)
27
+
28
+ @deprecation_warnings = value
18
29
  end
19
30
  end
20
31
  end
@@ -18,7 +18,7 @@ module JWT
18
18
  JWT::JWK::Thumbprint
19
19
  else
20
20
  raise ArgumentError, "#{value} is not a valid kid generator type."
21
- end
21
+ end
22
22
  end
23
23
 
24
24
  attr_accessor :kid_generator