jwt 2.4.1 → 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +102 -14
  3. data/CONTRIBUTING.md +7 -7
  4. data/README.md +154 -37
  5. data/lib/jwt/base64.rb +33 -0
  6. data/lib/jwt/claims_validator.rb +1 -1
  7. data/lib/jwt/configuration/container.rb +32 -0
  8. data/lib/jwt/configuration/decode_configuration.rb +46 -0
  9. data/lib/jwt/configuration/jwk_configuration.rb +27 -0
  10. data/lib/jwt/configuration.rb +15 -0
  11. data/lib/jwt/decode.rb +44 -29
  12. data/lib/jwt/deprecations.rb +29 -0
  13. data/lib/jwt/encode.rb +24 -20
  14. data/lib/jwt/error.rb +1 -0
  15. data/lib/jwt/{algos → jwa}/ecdsa.rb +19 -7
  16. data/lib/jwt/jwa/eddsa.rb +42 -0
  17. data/lib/jwt/jwa/hmac.rb +75 -0
  18. data/lib/jwt/jwa/hmac_rbnacl.rb +50 -0
  19. data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +46 -0
  20. data/lib/jwt/{algos → jwa}/none.rb +4 -2
  21. data/lib/jwt/jwa/ps.rb +30 -0
  22. data/lib/jwt/jwa/rsa.rb +25 -0
  23. data/lib/jwt/{algos → jwa}/unsupported.rb +1 -1
  24. data/lib/jwt/jwa/wrapper.rb +26 -0
  25. data/lib/jwt/jwa.rb +62 -0
  26. data/lib/jwt/jwk/ec.rb +160 -63
  27. data/lib/jwt/jwk/hmac.rb +69 -24
  28. data/lib/jwt/jwk/key_base.rb +45 -7
  29. data/lib/jwt/jwk/key_finder.rb +19 -35
  30. data/lib/jwt/jwk/kid_as_key_digest.rb +15 -0
  31. data/lib/jwt/jwk/okp_rbnacl.rb +110 -0
  32. data/lib/jwt/jwk/rsa.rb +141 -54
  33. data/lib/jwt/jwk/set.rb +80 -0
  34. data/lib/jwt/jwk/thumbprint.rb +26 -0
  35. data/lib/jwt/jwk.rb +14 -11
  36. data/lib/jwt/verify.rb +8 -4
  37. data/lib/jwt/version.rb +23 -1
  38. data/lib/jwt/x5c_key_finder.rb +1 -4
  39. data/lib/jwt.rb +6 -4
  40. data/ruby-jwt.gemspec +11 -4
  41. metadata +41 -27
  42. data/.codeclimate.yml +0 -8
  43. data/.github/workflows/coverage.yml +0 -27
  44. data/.github/workflows/test.yml +0 -66
  45. data/.gitignore +0 -13
  46. data/.reek.yml +0 -22
  47. data/.rspec +0 -2
  48. data/.rubocop.yml +0 -67
  49. data/.sourcelevel.yml +0 -17
  50. data/Appraisals +0 -13
  51. data/Gemfile +0 -7
  52. data/Rakefile +0 -16
  53. data/lib/jwt/algos/eddsa.rb +0 -33
  54. data/lib/jwt/algos/hmac.rb +0 -36
  55. data/lib/jwt/algos/ps.rb +0 -43
  56. data/lib/jwt/algos/rsa.rb +0 -22
  57. data/lib/jwt/algos.rb +0 -44
  58. data/lib/jwt/default_options.rb +0 -18
  59. data/lib/jwt/security_utils.rb +0 -59
  60. data/lib/jwt/signature.rb +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e7f3474ee58d51ca5646f48ca28bf669b40a4b7676cbe7211597ca6ae69f672
4
- data.tar.gz: 570e6930c9094afea40ea8e8a6a7c9b3293890b121893f5148914b0a8e7d11f8
3
+ metadata.gz: af3792b982f014801d3ff7ae3410be6bd1e0b27f199c500942eb86267bb2b764
4
+ data.tar.gz: c37fc4b72cf3819210b15164548eff44579de1e8f8c48d9ce35864a84f007d9a
5
5
  SHA512:
6
- metadata.gz: 3249529ec6bacc8e655e2830949af61c10e235a569f9dc67d3880335d5939b8afc56c180145d3e02dd09744288d50c31547338e105cf55ae4e0fbe237eb2a0e8
7
- data.tar.gz: dd415314a7bd048d8b2b5b630d5b7011128932bf207dc785ac6154748aff68836a1c39e766dc176e225c643fc406fe9fdc5c510b36dc939e36722e327d8fe92f
6
+ metadata.gz: 3f61fd13a1d56c657691abb4bfde3671a7d93b5c853785b804c0d119d27f4641685828eb9c65a8e4856487782530e791ecbce5f1a5f1cb61c883daff97a44367
7
+ data.tar.gz: ef36aa81991e28cb9d3df65f1ebbeb6599eb99dee8e1953aff1a6231e91c6a3f9633e3afd22fbbc6c09f6318c4e516ee7a908428eee303f3952fed890395b267
data/CHANGELOG.md CHANGED
@@ -1,31 +1,119 @@
1
1
  # Changelog
2
- ## [v2.4.1](https://github.com/jwt/ruby-jwt/tree/v2.4.1) (2022-06-07)
2
+
3
+ ## [v2.8.1](https://github.com/jwt/ruby-jwt/tree/v2.8.1) (2024-02-29)
4
+
5
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.8.0...v2.8.1)
6
+
7
+ **Features:**
8
+
9
+ - Configurable base64 decode behaviour [#589](https://github.com/jwt/ruby-jwt/pull/589) ([@anakinj](https://github.com/anakinj))
10
+
11
+ **Fixes and enhancements:**
12
+
13
+ - Output deprecation warnings once [#589](https://github.com/jwt/ruby-jwt/pull/589) ([@anakinj](https://github.com/anakinj))
14
+
15
+ ## [v2.8.0](https://github.com/jwt/ruby-jwt/tree/v2.8.0) (2024-02-17)
16
+
17
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.7.1...v2.8.0)
18
+
19
+ **Features:**
20
+
21
+ - Updated rubocop to 1.56 [#573](https://github.com/jwt/ruby-jwt/pull/573) ([@anakinj](https://github.com/anakinj))
22
+ - Run CI on Ruby 3.3 [#577](https://github.com/jwt/ruby-jwt/pull/577) ([@anakinj](https://github.com/anakinj))
23
+ - 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))
24
+ - Stop using RbNaCl for standard HMAC algorithms [#575](https://github.com/jwt/ruby-jwt/pull/575) ([@anakinj](https://github.com/anakinj))
25
+
26
+ **Fixes and enhancements:**
27
+
28
+ - Fix signature has expired error if payload is a string [#555](https://github.com/jwt/ruby-jwt/pull/555) ([@GobinathAL](https://github.com/GobinathAL))
29
+ - Fix key base equality and spaceship operators [#569](https://github.com/jwt/ruby-jwt/pull/569) ([@magneland](https://github.com/magneland))
30
+ - Remove explicit base64 require from x5c_key_finder [#580](https://github.com/jwt/ruby-jwt/pull/580) ([@anakinj](https://github.com/anakinj))
31
+ - Performance improvements and cleanup of tests [#581](https://github.com/jwt/ruby-jwt/pull/581) ([@anakinj](https://github.com/anakinj))
32
+ - Repair EC x/y coordinates when importing JWK [#585](https://github.com/jwt/ruby-jwt/pull/585) ([@julik](https://github.com/julik))
33
+ - Explicit dependency to the base64 gem [#582](https://github.com/jwt/ruby-jwt/pull/582) ([@anakinj](https://github.com/anakinj))
34
+ - Deprecation warning for decoding content not compliant with RFC 4648 [#582](https://github.com/jwt/ruby-jwt/pull/582) ([@anakinj](https://github.com/anakinj))
35
+ - Algorithms moved under the `::JWT::JWA` module ([@anakinj](https://github.com/anakinj))
36
+
37
+ ## [v2.7.1](https://github.com/jwt/ruby-jwt/tree/v2.8.0) (2023-06-09)
38
+
39
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.7.0...v2.7.1)
3
40
 
4
41
  **Fixes and enhancements:**
5
- - Raise JWT::DecodeError on invalid signature [\#484](https://github.com/jwt/ruby-jwt/pull/484) ([@freakyfelt!](https://github.com/freakyfelt!)).
42
+
43
+ - Handle invalid algorithm when decoding JWT [#559](https://github.com/jwt/ruby-jwt/pull/559) ([@nataliastanko](https://github.com/nataliastanko))
44
+ - Do not raise error when verifying bad HMAC signature [#563](https://github.com/jwt/ruby-jwt/pull/563) ([@hieuk09](https://github.com/hieuk09))
45
+
46
+ ## [v2.7.0](https://github.com/jwt/ruby-jwt/tree/v2.7.0) (2023-02-01)
47
+
48
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.6.0...v2.7.0)
49
+
50
+ **Features:**
51
+
52
+ - Support OKP (Ed25519) keys for JWKs [#540](https://github.com/jwt/ruby-jwt/pull/540) ([@anakinj](https://github.com/anakinj))
53
+ - JWK Sets can now be used for tokens with nil kid [#543](https://github.com/jwt/ruby-jwt/pull/543) ([@bellebaum](https://github.com/bellebaum))
54
+
55
+ **Fixes and enhancements:**
56
+
57
+ - Fix issue with multiple keys returned by keyfinder and multiple allowed algorithms [#545](https://github.com/jwt/ruby-jwt/pull/545) ([@mpospelov](https://github.com/mpospelov))
58
+ - Non-string `kid` header values are now rejected [#543](https://github.com/jwt/ruby-jwt/pull/543) ([@bellebaum](https://github.com/bellebaum))
59
+
60
+ ## [v2.6.0](https://github.com/jwt/ruby-jwt/tree/v2.6.0) (2022-12-22)
61
+
62
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.5.0...v2.6.0)
63
+
64
+ **Features:**
65
+
66
+ - Support custom algorithms by passing algorithm objects [#512](https://github.com/jwt/ruby-jwt/pull/512) ([@anakinj](https://github.com/anakinj))
67
+ - Support descriptive (not key related) JWK parameters [#520](https://github.com/jwt/ruby-jwt/pull/520) ([@bellebaum](https://github.com/bellebaum))
68
+ - Support for JSON Web Key Sets [#525](https://github.com/jwt/ruby-jwt/pull/525) ([@bellebaum](https://github.com/bellebaum))
69
+ - Support HMAC keys over 32 chars when using RbNaCl [#521](https://github.com/jwt/ruby-jwt/pull/521) ([@anakinj](https://github.com/anakinj))
70
+
71
+ **Fixes and enhancements:**
72
+
73
+ - 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))
74
+
75
+ ## [v2.5.0](https://github.com/jwt/ruby-jwt/tree/v2.5.0) (2022-08-25)
76
+
77
+ [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.4.1...v2.5.0)
78
+
79
+ **Features:**
80
+
81
+ - Support JWK thumbprints as key ids [#481](https://github.com/jwt/ruby-jwt/pull/481) ([@anakinj](https://github.com/anakinj))
82
+ - Support OpenSSL >= 3.0 [#496](https://github.com/jwt/ruby-jwt/pull/496) ([@anakinj](https://github.com/anakinj))
83
+
84
+ **Fixes and enhancements:**
85
+ - Bring back the old Base64 (RFC2045) deocode mechanisms [#488](https://github.com/jwt/ruby-jwt/pull/488) ([@anakinj](https://github.com/anakinj))
86
+ - Rescue RbNaCl exception for EdDSA wrong key [#491](https://github.com/jwt/ruby-jwt/pull/491) ([@n-studio](https://github.com/n-studio))
87
+ - 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))
88
+ - 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))
89
+
90
+ ## [v2.4.1](https://github.com/jwt/ruby-jwt/tree/v2.4.1) (2022-06-07)
6
91
 
7
92
  [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.4.0...v2.4.1)
8
93
 
94
+ **Fixes and enhancements:**
95
+ - Raise JWT::DecodeError on invalid signature [\#484](https://github.com/jwt/ruby-jwt/pull/484) ([@freakyfelt!](https://github.com/freakyfelt!))
96
+
9
97
  ## [v2.4.0](https://github.com/jwt/ruby-jwt/tree/v2.4.0) (2022-06-06)
10
98
 
11
99
  [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.3.0...v2.4.0)
12
100
 
13
101
  **Features:**
14
102
 
15
- - Dropped support for Ruby 2.5 and older [#453](https://github.com/jwt/ruby-jwt/pull/453) - [@anakinj](https://github.com/anakinj).
16
- - Use Ruby built-in url-safe base64 methods [#454](https://github.com/jwt/ruby-jwt/pull/454) - [@bdewater](https://github.com/bdewater).
17
- - Updated rubocop to 1.23.0 [#457](https://github.com/jwt/ruby-jwt/pull/457) - [@anakinj](https://github.com/anakinj).
18
- - Add x5c header key finder [#338](https://github.com/jwt/ruby-jwt/pull/338) - [@bdewater](https://github.com/bdewater).
19
- - Author driven changelog process [#463](https://github.com/jwt/ruby-jwt/pull/463) - [@anakinj](https://github.com/anakinj).
20
- - Allow regular expressions and procs to verify issuer [\#437](https://github.com/jwt/ruby-jwt/pull/437) ([rewritten](https://github.com/rewritten)).
21
- - Add Support to be able to verify from multiple keys [\#425](https://github.com/jwt/ruby-jwt/pull/425) ([ritikesh](https://github.com/ritikesh)).
103
+ - Dropped support for Ruby 2.5 and older [#453](https://github.com/jwt/ruby-jwt/pull/453) - ([@anakinj](https://github.com/anakinj))
104
+ - Use Ruby built-in url-safe base64 methods [#454](https://github.com/jwt/ruby-jwt/pull/454) - ([@bdewater](https://github.com/bdewater))
105
+ - Updated rubocop to 1.23.0 [#457](https://github.com/jwt/ruby-jwt/pull/457) - ([@anakinj](https://github.com/anakinj))
106
+ - Add x5c header key finder [#338](https://github.com/jwt/ruby-jwt/pull/338) - ([@bdewater](https://github.com/bdewater))
107
+ - Author driven changelog process [#463](https://github.com/jwt/ruby-jwt/pull/463) - ([@anakinj](https://github.com/anakinj))
108
+ - Allow regular expressions and procs to verify issuer [\#437](https://github.com/jwt/ruby-jwt/pull/437) ([rewritten](https://github.com/rewritten))
109
+ - Add Support to be able to verify from multiple keys [\#425](https://github.com/jwt/ruby-jwt/pull/425) ([ritikesh](https://github.com/ritikesh))
22
110
 
23
111
  **Fixes and enhancements:**
24
- - Readme: Typo fix re MissingRequiredClaim [\#451](https://github.com/jwt/ruby-jwt/pull/451) ([antonmorant](https://github.com/antonmorant)).
25
- - Fix RuboCop TODOs [\#476](https://github.com/jwt/ruby-jwt/pull/476) ([typhoon2099](https://github.com/typhoon2099)).
26
- - Make specific algorithms in README linkable [\#472](https://github.com/jwt/ruby-jwt/pull/472) ([milieu](https://github.com/milieu)).
27
- - Update note about supported JWK types [\#475](https://github.com/jwt/ruby-jwt/pull/475) ([dpashkevich](https://github.com/dpashkevich)).
28
- - Create CODE\_OF\_CONDUCT.md [\#449](https://github.com/jwt/ruby-jwt/pull/449) ([loic5](https://github.com/loic5)).
112
+ - Readme: Typo fix re MissingRequiredClaim [\#451](https://github.com/jwt/ruby-jwt/pull/451) ([antonmorant](https://github.com/antonmorant))
113
+ - Fix RuboCop TODOs [\#476](https://github.com/jwt/ruby-jwt/pull/476) ([typhoon2099](https://github.com/typhoon2099))
114
+ - Make specific algorithms in README linkable [\#472](https://github.com/jwt/ruby-jwt/pull/472) ([milieu](https://github.com/milieu))
115
+ - Update note about supported JWK types [\#475](https://github.com/jwt/ruby-jwt/pull/475) ([dpashkevich](https://github.com/dpashkevich))
116
+ - Create CODE\_OF\_CONDUCT.md [\#449](https://github.com/jwt/ruby-jwt/pull/449) ([loic5](https://github.com/loic5))
29
117
 
30
118
  ## [v2.3.0](https://github.com/jwt/ruby-jwt/tree/v2.3.0) (2021-10-03)
31
119
 
data/CONTRIBUTING.md CHANGED
@@ -12,19 +12,19 @@ git remote add upstream https://github.com/jwt/ruby-jwt
12
12
 
13
13
  ## Create a branch for your implementation
14
14
 
15
- Make sure you have the latest upstream master branch of the project.
15
+ Make sure you have the latest upstream main branch of the project.
16
16
 
17
17
  ```
18
18
  git fetch --all
19
- git checkout master
20
- git rebase upstream/master
21
- git push origin master
19
+ git checkout main
20
+ git rebase upstream/main
21
+ git push origin main
22
22
  git checkout -b fix-a-little-problem
23
23
  ```
24
24
 
25
25
  ## Running the tests and linter
26
26
 
27
- Before you start with your implementation make sure you are able to get a succesful test run with the current revision.
27
+ Before you start with your implementation make sure you are able to get a successful test run with the current revision.
28
28
 
29
29
  The tests are written with rspec and [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
30
30
 
@@ -78,12 +78,12 @@ A maintainer will review and probably merge you changes when time allows, be pat
78
78
 
79
79
  ## Keeping your branch up-to-date
80
80
 
81
- It's recommended that you keep your branch up-to-date by rebasing to the upstream master.
81
+ It's recommended that you keep your branch up-to-date by rebasing to the upstream main.
82
82
 
83
83
  ```
84
84
  git fetch upstream
85
85
  git checkout fix-a-little-problem
86
- git rebase upstream/master
86
+ git rebase upstream/main
87
87
  git push origin fix-a-little-problem -f
88
88
  ```
89
89
 
data/README.md CHANGED
@@ -1,18 +1,17 @@
1
1
  # JWT
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/jwt.svg)](https://badge.fury.io/rb/jwt)
4
- [![Build Status](https://github.com/jwt/ruby-jwt/workflows/test/badge.svg?branch=master)](https://github.com/jwt/ruby-jwt/actions)
4
+ [![Build Status](https://github.com/jwt/ruby-jwt/workflows/test/badge.svg?branch=main)](https://github.com/jwt/ruby-jwt/actions)
5
5
  [![Code Climate](https://codeclimate.com/github/jwt/ruby-jwt/badges/gpa.svg)](https://codeclimate.com/github/jwt/ruby-jwt)
6
6
  [![Test Coverage](https://codeclimate.com/github/jwt/ruby-jwt/badges/coverage.svg)](https://codeclimate.com/github/jwt/ruby-jwt/coverage)
7
7
  [![Issue Count](https://codeclimate.com/github/jwt/ruby-jwt/badges/issue_count.svg)](https://codeclimate.com/github/jwt/ruby-jwt)
8
- [![SourceLevel](https://app.sourcelevel.io/github/jwt/-/ruby-jwt.svg)](https://app.sourcelevel.io/github/jwt/-/ruby-jwt)
9
8
 
10
9
  A ruby implementation of the [RFC 7519 OAuth JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519) standard.
11
10
 
12
11
  If you have further questions related to development or usage, join us: [ruby-jwt google group](https://groups.google.com/forum/#!forum/ruby-jwt).
13
12
 
14
13
  ## Announcements
15
- * Ruby 2.4 support is going to be dropped in version 2.4.0
14
+ * Ruby 2.4 support was dropped in version 2.4.0
16
15
  * Ruby 1.9.3 support was dropped at December 31st, 2016.
17
16
  * Version 1.5.3 yanked. See: [#132](https://github.com/jwt/ruby-jwt/issues/132) and [#133](https://github.com/jwt/ruby-jwt/issues/133)
18
17
 
@@ -44,6 +43,23 @@ The JWT spec supports NONE, HMAC, RSASSA, ECDSA and RSASSA-PSS algorithms for cr
44
43
 
45
44
  See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1)
46
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
+
47
63
  ### **NONE**
48
64
 
49
65
  * none - unsigned token
@@ -73,12 +89,11 @@ puts decoded_token
73
89
  ### **HMAC**
74
90
 
75
91
  * HS256 - HMAC using SHA-256 hash algorithm
76
- * HS512256 - HMAC using SHA-512-256 hash algorithm (only available with RbNaCl; see note below)
77
92
  * HS384 - HMAC using SHA-384 hash algorithm
78
93
  * HS512 - HMAC using SHA-512 hash algorithm
79
94
 
80
95
  ```ruby
81
- # The secret must be a string. A JWT::DecodeError will be raised if it isn't provided.
96
+ # 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.
82
97
  hmac_secret = 'my$ecretK3y'
83
98
 
84
99
  token = JWT.encode payload, hmac_secret, 'HS256'
@@ -96,12 +111,6 @@ decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
96
111
  puts decoded_token
97
112
  ```
98
113
 
99
- Note: If [RbNaCl](https://github.com/cryptosphere/rbnacl) is loadable, ruby-jwt will use it for HMAC-SHA256, HMAC-SHA512-256, and HMAC-SHA512. RbNaCl enforces a maximum key size of 32 bytes for these algorithms.
100
-
101
- [RbNaCl](https://github.com/cryptosphere/rbnacl) requires
102
- [libsodium](https://github.com/jedisct1/libsodium), it can be installed
103
- on MacOS with `brew install libsodium`.
104
-
105
114
  ### **RSA**
106
115
 
107
116
  * RS256 - RSA using SHA-256 hash algorithm
@@ -135,17 +144,14 @@ puts decoded_token
135
144
  * ES256K - ECDSA using P-256K and SHA-256
136
145
 
137
146
  ```ruby
138
- ecdsa_key = OpenSSL::PKey::EC.new 'prime256v1'
139
- ecdsa_key.generate_key
140
- ecdsa_public = OpenSSL::PKey::EC.new ecdsa_key
141
- ecdsa_public.private_key = nil
147
+ ecdsa_key = OpenSSL::PKey::EC.generate('prime256v1')
142
148
 
143
149
  token = JWT.encode payload, ecdsa_key, 'ES256'
144
150
 
145
151
  # eyJhbGciOiJFUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.AlLW--kaF7EX1NMX9WJRuIW8NeRJbn2BLXHns7Q5TZr7Hy3lF6MOpMlp7GoxBFRLISQ6KrD0CJOrR8aogEsPeg
146
152
  puts token
147
153
 
148
- decoded_token = JWT.decode token, ecdsa_public, true, { algorithm: 'ES256' }
154
+ decoded_token = JWT.decode token, ecdsa_key, true, { algorithm: 'ES256' }
149
155
 
150
156
  # Array
151
157
  # [
@@ -163,7 +169,7 @@ In order to use this algorithm you need to add the `RbNaCl` gem to you `Gemfile`
163
169
  gem 'rbnacl'
164
170
  ```
165
171
 
166
- For more detailed installation instruction check the official [repository](https://github.com/cryptosphere/rbnacl) on GitHub.
172
+ For more detailed installation instruction check the official [repository](https://github.com/RubyCrypto/rbnacl) on GitHub.
167
173
 
168
174
  * ED25519
169
175
 
@@ -186,7 +192,7 @@ decoded_token = JWT.decode token, public_key, true, { algorithm: 'ED25519' }
186
192
 
187
193
  ### **RSASSA-PSS**
188
194
 
189
- In order to use this algorithm you need to add the `openssl` gem to you `Gemfile` with a version greater or equal to `2.1`.
195
+ 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`.
190
196
 
191
197
  ```ruby
192
198
  gem 'openssl', '~> 2.1'
@@ -215,6 +221,33 @@ decoded_token = JWT.decode token, rsa_public, true, { algorithm: 'PS256' }
215
221
  puts decoded_token
216
222
  ```
217
223
 
224
+ ### **Custom algorithms**
225
+
226
+ 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.
227
+
228
+ ```ruby
229
+ module CustomHS512Algorithm
230
+ def self.alg
231
+ 'HS512'
232
+ end
233
+
234
+ def self.valid_alg?(alg_to_validate)
235
+ alg_to_validate == alg
236
+ end
237
+
238
+ def self.sign(data:, signing_key:)
239
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha512'), data, signing_key)
240
+ end
241
+
242
+ def self.verify(data:, signature:, verification_key:)
243
+ ::OpenSSL.secure_compare(sign(data: data, signing_key: verification_key), signature)
244
+ end
245
+ end
246
+
247
+ token = ::JWT.encode({'pay' => 'load'}, 'secret', CustomHS512Algorithm)
248
+ payload, header = ::JWT.decode(token, 'secret', true, algorithm: CustomHS512Algorithm)
249
+ ```
250
+
218
251
  ## Support for reserved claim names
219
252
  JSON Web Token defines some reserved claim names and defines how they should be
220
253
  used. JWT supports these reserved claim names:
@@ -538,7 +571,7 @@ crls = crl_uris.map do |uri|
538
571
  end
539
572
 
540
573
  begin
541
- JWT.decode(token, nil, true, { x5c: { root_certificates: root_certificates, crls: crls })
574
+ JWT.decode(token, nil, true, { x5c: { root_certificates: root_certificates, crls: crls } })
542
575
  rescue JWT::DecodeError
543
576
  # Handle error, e.g. x5c header certificate revoked or expired
544
577
  end
@@ -546,22 +579,62 @@ end
546
579
 
547
580
  ### JSON Web Key (JWK)
548
581
 
549
- JWK is a JSON structure representing a cryptographic key. Currently only supports RSA, EC and HMAC keys.
582
+ JWK is a JSON structure representing a cryptographic key. This gem currently supports RSA, EC, OKP and HMAC keys. OKP support requires [RbNaCl](https://github.com/RubyCrypto/rbnacl) and currently only supports the Ed25519 curve.
583
+
584
+ To encode a JWT using your JWK:
585
+
586
+ ```ruby
587
+ optional_parameters = { kid: 'my-kid', use: 'sig', alg: 'RS512' }
588
+ jwk = JWT::JWK.new(OpenSSL::PKey::RSA.new(2048), optional_parameters)
589
+
590
+ # Encoding
591
+ payload = { data: 'data' }
592
+ token = JWT.encode(payload, jwk.signing_key, jwk[:alg], kid: jwk[:kid])
593
+
594
+ # JSON Web Key Set for advertising your signing keys
595
+ jwks_hash = JWT::JWK::Set.new(jwk).export
596
+ ```
597
+
598
+ To decode a JWT using a trusted entity's JSON Web Key Set (JWKS):
550
599
 
551
600
  ```ruby
552
- jwk = JWT::JWK.new(OpenSSL::PKey::RSA.new(2048), "optional-kid")
553
- payload, headers = { data: 'data' }, { kid: jwk.kid }
601
+ jwks = JWT::JWK::Set.new(jwks_hash)
602
+ jwks.filter! {|key| key[:use] == 'sig' } # Signing keys only!
603
+ algorithms = jwks.map { |key| key[:alg] }.compact.uniq
604
+ JWT.decode(token, nil, true, algorithms: algorithms, jwks: jwks)
605
+ ```
606
+
607
+
608
+ The `jwks` option can also be given as a lambda that evaluates every time a kid is resolved.
609
+ This can be used to implement caching of remotely fetched JWK Sets.
554
610
 
555
- token = JWT.encode(payload, jwk.keypair, 'RS512', headers)
611
+ If the requested `kid` is not found from the given set the loader will be called a second time with the `kid_not_found` option set to `true`.
612
+ The application can choose to implement some kind of JWK cache invalidation or other mechanism to handle such cases.
556
613
 
557
- # The jwk loader would fetch the set of JWKs from a trusted source
558
- jwk_loader = ->(options) do
559
- @cached_keys = nil if options[:invalidate] # need to reload the keys
560
- @cached_keys ||= { keys: [jwk.export] }
614
+ Tokens without a specified `kid` are rejected by default.
615
+ This behaviour may be overwritten by setting the `allow_nil_kid` option for `decode` to `true`.
616
+
617
+ ```ruby
618
+ jwks_loader = ->(options) do
619
+ # The jwk loader would fetch the set of JWKs from a trusted source.
620
+ # To avoid malicious requests triggering cache invalidations there needs to be
621
+ # some kind of grace time or other logic for determining the validity of the invalidation.
622
+ # This example only allows cache invalidations every 5 minutes.
623
+ if options[:kid_not_found] && @cache_last_update < Time.now.to_i - 300
624
+ logger.info("Invalidating JWK cache. #{options[:kid]} not found from previous cache")
625
+ @cached_keys = nil
626
+ end
627
+ @cached_keys ||= begin
628
+ @cache_last_update = Time.now.to_i
629
+ # Replace with your own JWKS fetching routine
630
+ jwks = JWT::JWK::Set.new(jwks_hash)
631
+ jwks.select! { |key| key[:use] == 'sig' } # Signing Keys only
632
+ jwks
633
+ end
561
634
  end
562
635
 
563
636
  begin
564
- JWT.decode(token, nil, true, { algorithms: ['RS512'], jwks: jwk_loader})
637
+ JWT.decode(token, nil, true, { algorithms: ['RS512'], jwks: jwks_loader })
565
638
  rescue JWT::JWKError
566
639
  # Handle problems with the provided JWKs
567
640
  rescue JWT::DecodeError
@@ -569,26 +642,70 @@ rescue JWT::DecodeError
569
642
  end
570
643
  ```
571
644
 
572
- or by passing JWK as a simple Hash
645
+ ### Importing and exporting JSON Web Keys
573
646
 
574
- ```
575
- jwks = { keys: [{ ... }] } # keys accepts both of string and symbol
576
- JWT.decode(token, nil, true, { algorithms: ['RS512'], jwks: jwks})
647
+ The ::JWT::JWK class can be used to import both JSON Web Keys and OpenSSL keys
648
+ and export to either format with and without the private key included.
649
+
650
+ To include the private key in the export pass the `include_private` parameter to the export method.
651
+
652
+ ```ruby
653
+ # Import a JWK Hash (showing an HMAC example)
654
+ jwk = JWT::JWK.new({ kty: 'oct', k: 'my-secret', kid: 'my-kid' })
655
+
656
+ # Import an OpenSSL key
657
+ # You can optionally add descriptive parameters to the JWK
658
+ desc_params = { kid: 'my-kid', use: 'sig' }
659
+ jwk = JWT::JWK.new(OpenSSL::PKey::RSA.new(2048), desc_params)
660
+
661
+ # Export as JWK Hash (public key only by default)
662
+ jwk_hash = jwk.export
663
+ jwk_hash_with_private_key = jwk.export(include_private: true)
664
+
665
+ # Export as OpenSSL key
666
+ public_key = jwk.verify_key
667
+ private_key = jwk.signing_key if jwk.private?
668
+
669
+ # You can also import and export entire JSON Web Key Sets
670
+ jwks_hash = { keys: [{ kty: 'oct', k: 'my-secret', kid: 'my-kid' }] }
671
+ jwks = JWT::JWK::Set.new(jwks_hash)
672
+ jwks_hash = jwks.export
577
673
  ```
578
674
 
579
- ### Importing and exporting JSON Web Keys
675
+ ### Key ID (kid) and JWKs
580
676
 
581
- The ::JWT::JWK class can be used to import and export both the public key (default behaviour) and the private key. To include the private key in the export pass the `include_private` parameter to the export method.
677
+ The key id (kid) generation in the gem is a custom algorithm and not based on any standards.
678
+ To use a standardized JWK thumbprint (RFC 7638) as the kid for JWKs a generator type can be specified in the global configuration
679
+ or can be given to the JWK instance on initialization.
582
680
 
583
681
  ```ruby
584
- jwk = JWT::JWK.new(OpenSSL::PKey::RSA.new(2048))
682
+ JWT.configuration.jwk.kid_generator_type = :rfc7638_thumbprint
683
+ # OR
684
+ JWT.configuration.jwk.kid_generator = ::JWT::JWK::Thumbprint
685
+ # OR
686
+ jwk = JWT::JWK.new(OpenSSL::PKey::RSA.new(2048), nil, kid_generator: ::JWT::JWK::Thumbprint)
585
687
 
586
688
  jwk_hash = jwk.export
587
- jwk_hash_with_private_key = jwk.export(include_private: true)
689
+
690
+ thumbprint_as_the_kid = jwk_hash[:kid]
588
691
  ```
589
692
 
590
- ## How to contribute
693
+ # Development and Tests
694
+
695
+ We depend on [Bundler](http://rubygems.org/gems/bundler) for defining gemspec and performing releases to rubygems.org, which can be done with
591
696
 
697
+ ```bash
698
+ rake release
699
+ ```
700
+
701
+ The tests are written with rspec. [Appraisal](https://github.com/thoughtbot/appraisal) is used to ensure compatibility with 3rd party dependencies providing cryptographic features.
702
+
703
+ ```bash
704
+ bundle install
705
+ bundle exec appraisal rake test
706
+ ```
707
+
708
+ ## How to contribute
592
709
  See [CONTRIBUTING](CONTRIBUTING.md).
593
710
 
594
711
  ## Contributors
data/lib/jwt/base64.rb ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module JWT
6
+ # Base64 encoding and decoding
7
+ class Base64
8
+ class << self
9
+ # Encode a string with URL-safe Base64 complying with RFC 4648 (not padded).
10
+ def url_encode(str)
11
+ ::Base64.urlsafe_encode64(str, padding: false)
12
+ end
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")
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')
24
+ end
25
+ end
26
+
27
+ def loose_urlsafe_decode64(str)
28
+ str += '=' * (4 - str.length.modulo(4))
29
+ ::Base64.decode64(str.tr('-_', '+/'))
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './error'
3
+ require_relative 'error'
4
4
 
5
5
  module JWT
6
6
  class ClaimsValidator
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'decode_configuration'
4
+ require_relative 'jwk_configuration'
5
+
6
+ module JWT
7
+ module Configuration
8
+ class Container
9
+ attr_accessor :decode, :jwk, :strict_base64_decoding
10
+ attr_reader :deprecation_warnings
11
+
12
+ def initialize
13
+ reset!
14
+ end
15
+
16
+ def reset!
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
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Configuration
5
+ class DecodeConfiguration
6
+ attr_accessor :verify_expiration,
7
+ :verify_not_before,
8
+ :verify_iss,
9
+ :verify_iat,
10
+ :verify_jti,
11
+ :verify_aud,
12
+ :verify_sub,
13
+ :leeway,
14
+ :algorithms,
15
+ :required_claims
16
+
17
+ def initialize
18
+ @verify_expiration = true
19
+ @verify_not_before = true
20
+ @verify_iss = false
21
+ @verify_iat = false
22
+ @verify_jti = false
23
+ @verify_aud = false
24
+ @verify_sub = false
25
+ @leeway = 0
26
+ @algorithms = ['HS256']
27
+ @required_claims = []
28
+ end
29
+
30
+ def to_h
31
+ {
32
+ verify_expiration: verify_expiration,
33
+ verify_not_before: verify_not_before,
34
+ verify_iss: verify_iss,
35
+ verify_iat: verify_iat,
36
+ verify_jti: verify_jti,
37
+ verify_aud: verify_aud,
38
+ verify_sub: verify_sub,
39
+ leeway: leeway,
40
+ algorithms: algorithms,
41
+ required_claims: required_claims
42
+ }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../jwk/kid_as_key_digest'
4
+ require_relative '../jwk/thumbprint'
5
+
6
+ module JWT
7
+ module Configuration
8
+ class JwkConfiguration
9
+ def initialize
10
+ self.kid_generator_type = :key_digest
11
+ end
12
+
13
+ def kid_generator_type=(value)
14
+ self.kid_generator = case value
15
+ when :key_digest
16
+ JWT::JWK::KidAsKeyDigest
17
+ when :rfc7638_thumbprint
18
+ JWT::JWK::Thumbprint
19
+ else
20
+ raise ArgumentError, "#{value} is not a valid kid generator type."
21
+ end
22
+ end
23
+
24
+ attr_accessor :kid_generator
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configuration/container'
4
+
5
+ module JWT
6
+ module Configuration
7
+ def configure
8
+ yield(configuration)
9
+ end
10
+
11
+ def configuration
12
+ @configuration ||= ::JWT::Configuration::Container.new
13
+ end
14
+ end
15
+ end