jwt 2.7.1 → 2.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +61 -27
- data/README.md +18 -8
- data/lib/jwt/base64.rb +16 -2
- data/lib/jwt/claims_validator.rb +1 -1
- data/lib/jwt/configuration/container.rb +14 -3
- data/lib/jwt/decode.rb +1 -7
- data/lib/jwt/deprecations.rb +29 -0
- data/lib/jwt/encode.rb +2 -8
- data/lib/jwt/error.rb +1 -0
- data/lib/jwt/{algos → jwa}/ecdsa.rb +1 -1
- data/lib/jwt/jwa/eddsa.rb +42 -0
- data/lib/jwt/{algos → jwa}/hmac.rb +3 -1
- data/lib/jwt/jwa/hmac_rbnacl.rb +50 -0
- data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +46 -0
- data/lib/jwt/{algos → jwa}/none.rb +1 -1
- data/lib/jwt/{algos → jwa}/ps.rb +4 -15
- data/lib/jwt/{algos → jwa}/rsa.rb +4 -2
- data/lib/jwt/{algos → jwa}/unsupported.rb +1 -1
- data/lib/jwt/{algos/algo_wrapper.rb → jwa/wrapper.rb} +2 -2
- data/lib/jwt/jwa.rb +62 -0
- data/lib/jwt/jwk/ec.rb +22 -7
- data/lib/jwt/jwk/key_base.rb +3 -1
- data/lib/jwt/jwk.rb +1 -1
- data/lib/jwt/verify.rb +8 -4
- data/lib/jwt/version.rb +3 -2
- data/lib/jwt/x5c_key_finder.rb +0 -3
- data/lib/jwt.rb +1 -0
- data/ruby-jwt.gemspec +3 -0
- metadata +43 -14
- data/lib/jwt/algos/eddsa.rb +0 -33
- data/lib/jwt/algos/hmac_rbnacl.rb +0 -53
- data/lib/jwt/algos/hmac_rbnacl_fixed.rb +0 -52
- data/lib/jwt/algos.rb +0 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af3792b982f014801d3ff7ae3410be6bd1e0b27f199c500942eb86267bb2b764
|
4
|
+
data.tar.gz: c37fc4b72cf3819210b15164548eff44579de1e8f8c48d9ce35864a84f007d9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f61fd13a1d56c657691abb4bfde3671a7d93b5c853785b804c0d119d27f4641685828eb9c65a8e4856487782530e791ecbce5f1a5f1cb61c883daff97a44367
|
7
|
+
data.tar.gz: ef36aa81991e28cb9d3df65f1ebbeb6599eb99dee8e1953aff1a6231e91c6a3f9633e3afd22fbbc6c09f6318c4e516ee7a908428eee303f3952fed890395b267
|
data/CHANGELOG.md
CHANGED
@@ -1,13 +1,47 @@
|
|
1
1
|
# Changelog
|
2
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
|
+
|
3
37
|
## [v2.7.1](https://github.com/jwt/ruby-jwt/tree/v2.8.0) (2023-06-09)
|
4
38
|
|
5
|
-
[Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.7.0...v2.
|
39
|
+
[Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.7.0...v2.7.1)
|
6
40
|
|
7
41
|
**Fixes and enhancements:**
|
8
42
|
|
9
|
-
- Handle invalid algorithm when decoding JWT [#559](https://github.com/jwt/ruby-jwt/pull/559)
|
10
|
-
- Do not raise error when verifying bad HMAC signature [#563](https://github.com/jwt/ruby-jwt/pull/563)
|
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))
|
11
45
|
|
12
46
|
## [v2.7.0](https://github.com/jwt/ruby-jwt/tree/v2.7.0) (2023-02-01)
|
13
47
|
|
@@ -29,14 +63,14 @@
|
|
29
63
|
|
30
64
|
**Features:**
|
31
65
|
|
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))
|
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))
|
36
70
|
|
37
71
|
**Fixes and enhancements:**
|
38
72
|
|
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))
|
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))
|
40
74
|
|
41
75
|
## [v2.5.0](https://github.com/jwt/ruby-jwt/tree/v2.5.0) (2022-08-25)
|
42
76
|
|
@@ -44,21 +78,21 @@
|
|
44
78
|
|
45
79
|
**Features:**
|
46
80
|
|
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))
|
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))
|
49
83
|
|
50
84
|
**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))
|
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))
|
55
89
|
|
56
90
|
## [v2.4.1](https://github.com/jwt/ruby-jwt/tree/v2.4.1) (2022-06-07)
|
57
91
|
|
58
92
|
[Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.4.0...v2.4.1)
|
59
93
|
|
60
94
|
**Fixes and enhancements:**
|
61
|
-
- Raise JWT::DecodeError on invalid signature [\#484](https://github.com/jwt/ruby-jwt/pull/484) ([@freakyfelt!](https://github.com/freakyfelt!))
|
95
|
+
- Raise JWT::DecodeError on invalid signature [\#484](https://github.com/jwt/ruby-jwt/pull/484) ([@freakyfelt!](https://github.com/freakyfelt!))
|
62
96
|
|
63
97
|
## [v2.4.0](https://github.com/jwt/ruby-jwt/tree/v2.4.0) (2022-06-06)
|
64
98
|
|
@@ -66,20 +100,20 @@
|
|
66
100
|
|
67
101
|
**Features:**
|
68
102
|
|
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))
|
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))
|
76
110
|
|
77
111
|
**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))
|
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))
|
83
117
|
|
84
118
|
## [v2.3.0](https://github.com/jwt/ruby-jwt/tree/v2.3.0) (2021-10-03)
|
85
119
|
|
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
|
@@ -561,7 +571,7 @@ crls = crl_uris.map do |uri|
|
|
561
571
|
end
|
562
572
|
|
563
573
|
begin
|
564
|
-
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 } })
|
565
575
|
rescue JWT::DecodeError
|
566
576
|
# Handle error, e.g. x5c header certificate revoked or expired
|
567
577
|
end
|
data/lib/jwt/base64.rb
CHANGED
@@ -3,14 +3,28 @@
|
|
3
3
|
require 'base64'
|
4
4
|
|
5
5
|
module JWT
|
6
|
-
# Base64
|
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.
|
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')
|
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
|
data/lib/jwt/claims_validator.rb
CHANGED
@@ -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
|
17
|
-
@jwk
|
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
|
data/lib/jwt/decode.rb
CHANGED
@@ -92,13 +92,7 @@ module JWT
|
|
92
92
|
end
|
93
93
|
|
94
94
|
def resolve_allowed_algorithms
|
95
|
-
algs = given_algorithms.map
|
96
|
-
if Algos.implementation?(alg)
|
97
|
-
alg
|
98
|
-
else
|
99
|
-
Algos.create(alg)
|
100
|
-
end
|
101
|
-
end
|
95
|
+
algs = given_algorithms.map { |alg| JWA.create(alg) }
|
102
96
|
|
103
97
|
sort_by_alg_header(algs)
|
104
98
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
# Deprecations module to handle deprecation warnings in the gem
|
5
|
+
module Deprecations
|
6
|
+
class << self
|
7
|
+
def warning(message)
|
8
|
+
case JWT.configuration.deprecation_warnings
|
9
|
+
when :warn
|
10
|
+
warn("[DEPRECATION WARNING] #{message}")
|
11
|
+
when :once
|
12
|
+
return if record_warned(message)
|
13
|
+
|
14
|
+
warn("[DEPRECATION WARNING] #{message}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def record_warned(message)
|
21
|
+
@warned ||= []
|
22
|
+
return true if @warned.include?(message)
|
23
|
+
|
24
|
+
@warned << message
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/jwt/encode.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'jwa'
|
4
4
|
require_relative 'claims_validator'
|
5
5
|
|
6
6
|
# JWT::Encode module
|
@@ -12,7 +12,7 @@ module JWT
|
|
12
12
|
def initialize(options)
|
13
13
|
@payload = options[:payload]
|
14
14
|
@key = options[:key]
|
15
|
-
@algorithm =
|
15
|
+
@algorithm = JWA.create(options[:algorithm])
|
16
16
|
@headers = options[:headers].transform_keys(&:to_s)
|
17
17
|
@headers[ALG_KEY] = @algorithm.alg
|
18
18
|
end
|
@@ -24,12 +24,6 @@ module JWT
|
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
-
def resolve_algorithm(algorithm)
|
28
|
-
return algorithm if Algos.implementation?(algorithm)
|
29
|
-
|
30
|
-
Algos.create(algorithm)
|
31
|
-
end
|
32
|
-
|
33
27
|
def encoded_header
|
34
28
|
@encoded_header ||= encode_header
|
35
29
|
end
|
data/lib/jwt/error.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module JWA
|
5
|
+
module Eddsa
|
6
|
+
SUPPORTED = %w[ED25519 EdDSA].freeze
|
7
|
+
SUPPORTED_DOWNCASED = SUPPORTED.map(&:downcase).freeze
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def sign(algorithm, msg, key)
|
11
|
+
unless key.is_a?(RbNaCl::Signatures::Ed25519::SigningKey)
|
12
|
+
raise EncodeError, "Key given is a #{key.class} but has to be an RbNaCl::Signatures::Ed25519::SigningKey"
|
13
|
+
end
|
14
|
+
|
15
|
+
validate_algorithm!(algorithm)
|
16
|
+
|
17
|
+
key.sign(msg)
|
18
|
+
end
|
19
|
+
|
20
|
+
def verify(algorithm, public_key, signing_input, signature)
|
21
|
+
unless public_key.is_a?(RbNaCl::Signatures::Ed25519::VerifyKey)
|
22
|
+
raise DecodeError, "key given is a #{public_key.class} but has to be a RbNaCl::Signatures::Ed25519::VerifyKey"
|
23
|
+
end
|
24
|
+
|
25
|
+
validate_algorithm!(algorithm)
|
26
|
+
|
27
|
+
public_key.verify(signature, signing_input)
|
28
|
+
rescue RbNaCl::CryptoError
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def validate_algorithm!(algorithm)
|
35
|
+
return if SUPPORTED_DOWNCASED.include?(algorithm.downcase)
|
36
|
+
|
37
|
+
raise IncorrectAlgorithm, "Algorithm #{algorithm} not supported. Supported algoritms are #{SUPPORTED.join(', ')}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JWT
|
4
|
-
module
|
4
|
+
module JWA
|
5
5
|
module Hmac
|
6
6
|
module_function
|
7
7
|
|
@@ -44,6 +44,7 @@ module JWT
|
|
44
44
|
OpenSSL.fixed_length_secure_compare(a, b)
|
45
45
|
end
|
46
46
|
else
|
47
|
+
# :nocov:
|
47
48
|
def fixed_length_secure_compare(a, b)
|
48
49
|
raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
|
49
50
|
|
@@ -53,6 +54,7 @@ module JWT
|
|
53
54
|
b.each_byte { |byte| res |= byte ^ l.shift }
|
54
55
|
res == 0
|
55
56
|
end
|
57
|
+
# :nocov:
|
56
58
|
end
|
57
59
|
module_function :fixed_length_secure_compare
|
58
60
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module Algos
|
5
|
+
module HmacRbNaCl
|
6
|
+
MAPPING = { 'HS512256' => ::RbNaCl::HMAC::SHA512256 }.freeze
|
7
|
+
SUPPORTED = MAPPING.keys
|
8
|
+
class << self
|
9
|
+
def sign(algorithm, msg, key)
|
10
|
+
Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
|
11
|
+
if (hmac = resolve_algorithm(algorithm))
|
12
|
+
hmac.auth(key_for_rbnacl(hmac, key).encode('binary'), msg.encode('binary'))
|
13
|
+
else
|
14
|
+
Hmac.sign(algorithm, msg, key)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def verify(algorithm, key, signing_input, signature)
|
19
|
+
Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
|
20
|
+
if (hmac = resolve_algorithm(algorithm))
|
21
|
+
hmac.verify(key_for_rbnacl(hmac, key).encode('binary'), signature.encode('binary'), signing_input.encode('binary'))
|
22
|
+
else
|
23
|
+
Hmac.verify(algorithm, key, signing_input, signature)
|
24
|
+
end
|
25
|
+
rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def key_for_rbnacl(hmac, key)
|
32
|
+
key ||= ''
|
33
|
+
raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
|
34
|
+
|
35
|
+
return padded_empty_key(hmac.key_bytes) if key == ''
|
36
|
+
|
37
|
+
key
|
38
|
+
end
|
39
|
+
|
40
|
+
def resolve_algorithm(algorithm)
|
41
|
+
MAPPING.fetch(algorithm)
|
42
|
+
end
|
43
|
+
|
44
|
+
def padded_empty_key(length)
|
45
|
+
Array.new(length, 0x0).pack('C*').encode('binary')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
module Algos
|
5
|
+
module HmacRbNaClFixed
|
6
|
+
MAPPING = { 'HS512256' => ::RbNaCl::HMAC::SHA512256 }.freeze
|
7
|
+
SUPPORTED = MAPPING.keys
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def sign(algorithm, msg, key)
|
11
|
+
key ||= ''
|
12
|
+
Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
|
13
|
+
raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
|
14
|
+
|
15
|
+
if (hmac = resolve_algorithm(algorithm)) && key.bytesize <= hmac.key_bytes
|
16
|
+
hmac.auth(padded_key_bytes(key, hmac.key_bytes), msg.encode('binary'))
|
17
|
+
else
|
18
|
+
Hmac.sign(algorithm, msg, key)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def verify(algorithm, key, signing_input, signature)
|
23
|
+
key ||= ''
|
24
|
+
Deprecations.warning("The use of the algorithm #{algorithm} is deprecated and will be removed in the next major version of ruby-jwt")
|
25
|
+
raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
|
26
|
+
|
27
|
+
if (hmac = resolve_algorithm(algorithm)) && key.bytesize <= hmac.key_bytes
|
28
|
+
hmac.verify(padded_key_bytes(key, hmac.key_bytes), signature.encode('binary'), signing_input.encode('binary'))
|
29
|
+
else
|
30
|
+
Hmac.verify(algorithm, key, signing_input, signature)
|
31
|
+
end
|
32
|
+
rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def resolve_algorithm(algorithm)
|
37
|
+
MAPPING.fetch(algorithm)
|
38
|
+
end
|
39
|
+
|
40
|
+
def padded_key_bytes(key, bytesize)
|
41
|
+
key.bytes.fill(0, key.bytesize...bytesize).pack('C*')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/jwt/{algos → jwa}/ps.rb
RENAMED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JWT
|
4
|
-
module
|
4
|
+
module JWA
|
5
5
|
module Ps
|
6
6
|
# RSASSA-PSS signing algorithms
|
7
7
|
|
@@ -10,9 +10,9 @@ module JWT
|
|
10
10
|
SUPPORTED = %w[PS256 PS384 PS512].freeze
|
11
11
|
|
12
12
|
def sign(algorithm, msg, key)
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
unless key.is_a?(::OpenSSL::PKey::RSA)
|
14
|
+
raise EncodeError, "The given key is a #{key_class}. It has to be an OpenSSL::PKey::RSA instance."
|
15
|
+
end
|
16
16
|
|
17
17
|
translated_algorithm = algorithm.sub('PS', 'sha')
|
18
18
|
|
@@ -20,22 +20,11 @@ module JWT
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def verify(algorithm, public_key, signing_input, signature)
|
23
|
-
require_openssl!
|
24
23
|
translated_algorithm = algorithm.sub('PS', 'sha')
|
25
24
|
public_key.verify_pss(translated_algorithm, signature, signing_input, salt_length: :auto, mgf1_hash: translated_algorithm)
|
26
25
|
rescue OpenSSL::PKey::PKeyError
|
27
26
|
raise JWT::VerificationError, 'Signature verification raised'
|
28
27
|
end
|
29
|
-
|
30
|
-
def require_openssl!
|
31
|
-
if Object.const_defined?('OpenSSL')
|
32
|
-
if ::Gem::Version.new(OpenSSL::VERSION) < ::Gem::Version.new('2.1')
|
33
|
-
raise JWT::RequiredDependencyError, "You currently have OpenSSL #{OpenSSL::VERSION}. PS support requires >= 2.1"
|
34
|
-
end
|
35
|
-
else
|
36
|
-
raise JWT::RequiredDependencyError, 'PS signing requires OpenSSL +2.1'
|
37
|
-
end
|
38
|
-
end
|
39
28
|
end
|
40
29
|
end
|
41
30
|
end
|
@@ -1,14 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JWT
|
4
|
-
module
|
4
|
+
module JWA
|
5
5
|
module Rsa
|
6
6
|
module_function
|
7
7
|
|
8
8
|
SUPPORTED = %w[RS256 RS384 RS512].freeze
|
9
9
|
|
10
10
|
def sign(algorithm, msg, key)
|
11
|
-
|
11
|
+
unless key.is_a?(OpenSSL::PKey::RSA)
|
12
|
+
raise EncodeError, "The given key is a #{key.class}. It has to be an OpenSSL::PKey::RSA instance"
|
13
|
+
end
|
12
14
|
|
13
15
|
key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
|
14
16
|
end
|
data/lib/jwt/jwa.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'rbnacl'
|
7
|
+
rescue LoadError
|
8
|
+
raise if defined?(RbNaCl)
|
9
|
+
end
|
10
|
+
|
11
|
+
require_relative 'jwa/hmac'
|
12
|
+
require_relative 'jwa/eddsa'
|
13
|
+
require_relative 'jwa/ecdsa'
|
14
|
+
require_relative 'jwa/rsa'
|
15
|
+
require_relative 'jwa/ps'
|
16
|
+
require_relative 'jwa/none'
|
17
|
+
require_relative 'jwa/unsupported'
|
18
|
+
require_relative 'jwa/wrapper'
|
19
|
+
|
20
|
+
module JWT
|
21
|
+
module JWA
|
22
|
+
ALGOS = [Hmac, Ecdsa, Rsa, Eddsa, Ps, None, Unsupported].tap do |l|
|
23
|
+
if ::JWT.rbnacl_6_or_greater?
|
24
|
+
require_relative 'jwa/hmac_rbnacl'
|
25
|
+
l << Algos::HmacRbNaCl
|
26
|
+
elsif ::JWT.rbnacl?
|
27
|
+
require_relative 'jwa/hmac_rbnacl_fixed'
|
28
|
+
l << Algos::HmacRbNaClFixed
|
29
|
+
end
|
30
|
+
end.freeze
|
31
|
+
|
32
|
+
class << self
|
33
|
+
def find(algorithm)
|
34
|
+
indexed[algorithm&.downcase]
|
35
|
+
end
|
36
|
+
|
37
|
+
def create(algorithm)
|
38
|
+
return algorithm if JWA.implementation?(algorithm)
|
39
|
+
|
40
|
+
Wrapper.new(*find(algorithm))
|
41
|
+
end
|
42
|
+
|
43
|
+
def implementation?(algorithm)
|
44
|
+
(algorithm.respond_to?(:valid_alg?) && algorithm.respond_to?(:verify)) ||
|
45
|
+
(algorithm.respond_to?(:alg) && algorithm.respond_to?(:sign))
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def indexed
|
51
|
+
@indexed ||= begin
|
52
|
+
fallback = [nil, Unsupported]
|
53
|
+
ALGOS.each_with_object(Hash.new(fallback)) do |cls, hash|
|
54
|
+
cls.const_get(:SUPPORTED).each do |alg|
|
55
|
+
hash[alg.downcase] = [alg, cls]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/jwt/jwk/ec.rb
CHANGED
@@ -11,6 +11,7 @@ module JWT
|
|
11
11
|
EC_PUBLIC_KEY_ELEMENTS = %i[kty crv x y].freeze
|
12
12
|
EC_PRIVATE_KEY_ELEMENTS = %i[d].freeze
|
13
13
|
EC_KEY_ELEMENTS = (EC_PRIVATE_KEY_ELEMENTS + EC_PUBLIC_KEY_ELEMENTS).freeze
|
14
|
+
ZERO_BYTE = "\0".b.freeze
|
14
15
|
|
15
16
|
def initialize(key, params = nil, options = {})
|
16
17
|
params ||= {}
|
@@ -143,7 +144,6 @@ module JWT
|
|
143
144
|
if ::JWT.openssl_3?
|
144
145
|
def create_ec_key(jwk_crv, jwk_x, jwk_y, jwk_d) # rubocop:disable Metrics/MethodLength
|
145
146
|
curve = EC.to_openssl_curve(jwk_crv)
|
146
|
-
|
147
147
|
x_octets = decode_octets(jwk_x)
|
148
148
|
y_octets = decode_octets(jwk_y)
|
149
149
|
|
@@ -205,12 +205,27 @@ module JWT
|
|
205
205
|
end
|
206
206
|
end
|
207
207
|
|
208
|
-
def decode_octets(
|
209
|
-
::JWT::Base64.url_decode(
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
208
|
+
def decode_octets(base64_encoded_coordinate)
|
209
|
+
bytes = ::JWT::Base64.url_decode(base64_encoded_coordinate)
|
210
|
+
# Some base64 encoders on some platform omit a single 0-byte at
|
211
|
+
# the start of either Y or X coordinate of the elliptic curve point.
|
212
|
+
# This leads to an encoding error when data is passed to OpenSSL BN.
|
213
|
+
# It is know to have happend to exported JWKs on a Java application and
|
214
|
+
# on a Flutter/Dart application (both iOS and Android). All that is
|
215
|
+
# needed to fix the problem is adding a leading 0-byte. We know the
|
216
|
+
# required byte is 0 because with any other byte the point is no longer
|
217
|
+
# on the curve - and OpenSSL will actually communicate this via another
|
218
|
+
# exception. The indication of a stripped byte will be the fact that the
|
219
|
+
# coordinates - once decoded into bytes - should always be an even
|
220
|
+
# bytesize. For example, with a P-521 curve, both x and y must be 66 bytes.
|
221
|
+
# With a P-256 curve, both x and y must be 32 and so on. The simplest way
|
222
|
+
# to check for this truncation is thus to check whether the number of bytes
|
223
|
+
# is odd, and restore the leading 0-byte if it is.
|
224
|
+
if bytes.bytesize.odd?
|
225
|
+
ZERO_BYTE + bytes
|
226
|
+
else
|
227
|
+
bytes
|
228
|
+
end
|
214
229
|
end
|
215
230
|
|
216
231
|
class << self
|
data/lib/jwt/jwk/key_base.rb
CHANGED
@@ -38,12 +38,14 @@ module JWT
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def ==(other)
|
41
|
-
self[:kid] == other[:kid]
|
41
|
+
other.is_a?(::JWT::JWK::KeyBase) && self[:kid] == other[:kid]
|
42
42
|
end
|
43
43
|
|
44
44
|
alias eql? ==
|
45
45
|
|
46
46
|
def <=>(other)
|
47
|
+
return nil unless other.is_a?(::JWT::JWK::KeyBase)
|
48
|
+
|
47
49
|
self[:kid] <=> other[:kid]
|
48
50
|
end
|
49
51
|
|
data/lib/jwt/jwk.rb
CHANGED
data/lib/jwt/verify.rb
CHANGED
@@ -38,12 +38,12 @@ module JWT
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def verify_expiration
|
41
|
-
return unless @payload
|
41
|
+
return unless contains_key?(@payload, 'exp')
|
42
42
|
raise(JWT::ExpiredSignature, 'Signature has expired') if @payload['exp'].to_i <= (Time.now.to_i - exp_leeway)
|
43
43
|
end
|
44
44
|
|
45
45
|
def verify_iat
|
46
|
-
return unless @payload
|
46
|
+
return unless contains_key?(@payload, 'iat')
|
47
47
|
|
48
48
|
iat = @payload['iat']
|
49
49
|
raise(JWT::InvalidIatError, 'Invalid iat') if !iat.is_a?(Numeric) || iat.to_f > Time.now.to_f
|
@@ -77,7 +77,7 @@ module JWT
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def verify_not_before
|
80
|
-
return unless @payload
|
80
|
+
return unless contains_key?(@payload, 'nbf')
|
81
81
|
raise(JWT::ImmatureSignature, 'Signature nbf has not been reached') if @payload['nbf'].to_i > (Time.now.to_i + nbf_leeway)
|
82
82
|
end
|
83
83
|
|
@@ -92,7 +92,7 @@ module JWT
|
|
92
92
|
return unless (options_required_claims = @options[:required_claims])
|
93
93
|
|
94
94
|
options_required_claims.each do |required_claim|
|
95
|
-
raise(JWT::MissingRequiredClaim, "Missing required claim #{required_claim}") unless @payload
|
95
|
+
raise(JWT::MissingRequiredClaim, "Missing required claim #{required_claim}") unless contains_key?(@payload, required_claim)
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
@@ -109,5 +109,9 @@ module JWT
|
|
109
109
|
def nbf_leeway
|
110
110
|
@options[:nbf_leeway] || global_leeway
|
111
111
|
end
|
112
|
+
|
113
|
+
def contains_key?(payload, key)
|
114
|
+
payload.respond_to?(:key?) && payload.key?(key)
|
115
|
+
end
|
112
116
|
end
|
113
117
|
end
|
data/lib/jwt/version.rb
CHANGED
@@ -11,7 +11,7 @@ module JWT
|
|
11
11
|
# major version
|
12
12
|
MAJOR = 2
|
13
13
|
# minor version
|
14
|
-
MINOR =
|
14
|
+
MINOR = 8
|
15
15
|
# tiny version
|
16
16
|
TINY = 1
|
17
17
|
# alpha, beta, etc. tag
|
@@ -23,7 +23,8 @@ module JWT
|
|
23
23
|
|
24
24
|
def self.openssl_3?
|
25
25
|
return false if OpenSSL::OPENSSL_VERSION.include?('LibreSSL')
|
26
|
-
|
26
|
+
|
27
|
+
true if 3 * 0x10000000 <= OpenSSL::OPENSSL_VERSION_NUMBER
|
27
28
|
end
|
28
29
|
|
29
30
|
def self.rbnacl?
|
data/lib/jwt/x5c_key_finder.rb
CHANGED
data/lib/jwt.rb
CHANGED
data/ruby-jwt.gemspec
CHANGED
@@ -31,9 +31,12 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.executables = []
|
32
32
|
spec.require_paths = %w[lib]
|
33
33
|
|
34
|
+
spec.add_dependency 'base64'
|
35
|
+
|
34
36
|
spec.add_development_dependency 'appraisal'
|
35
37
|
spec.add_development_dependency 'bundler'
|
36
38
|
spec.add_development_dependency 'rake'
|
37
39
|
spec.add_development_dependency 'rspec'
|
40
|
+
spec.add_development_dependency 'rubocop'
|
38
41
|
spec.add_development_dependency 'simplecov'
|
39
42
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jwt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Rudat
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: base64
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: appraisal
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +80,20 @@ dependencies:
|
|
66
80
|
- - ">="
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
name: simplecov
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,17 +122,6 @@ files:
|
|
94
122
|
- LICENSE
|
95
123
|
- README.md
|
96
124
|
- lib/jwt.rb
|
97
|
-
- lib/jwt/algos.rb
|
98
|
-
- lib/jwt/algos/algo_wrapper.rb
|
99
|
-
- lib/jwt/algos/ecdsa.rb
|
100
|
-
- lib/jwt/algos/eddsa.rb
|
101
|
-
- lib/jwt/algos/hmac.rb
|
102
|
-
- lib/jwt/algos/hmac_rbnacl.rb
|
103
|
-
- lib/jwt/algos/hmac_rbnacl_fixed.rb
|
104
|
-
- lib/jwt/algos/none.rb
|
105
|
-
- lib/jwt/algos/ps.rb
|
106
|
-
- lib/jwt/algos/rsa.rb
|
107
|
-
- lib/jwt/algos/unsupported.rb
|
108
125
|
- lib/jwt/base64.rb
|
109
126
|
- lib/jwt/claims_validator.rb
|
110
127
|
- lib/jwt/configuration.rb
|
@@ -112,9 +129,21 @@ files:
|
|
112
129
|
- lib/jwt/configuration/decode_configuration.rb
|
113
130
|
- lib/jwt/configuration/jwk_configuration.rb
|
114
131
|
- lib/jwt/decode.rb
|
132
|
+
- lib/jwt/deprecations.rb
|
115
133
|
- lib/jwt/encode.rb
|
116
134
|
- lib/jwt/error.rb
|
117
135
|
- lib/jwt/json.rb
|
136
|
+
- lib/jwt/jwa.rb
|
137
|
+
- lib/jwt/jwa/ecdsa.rb
|
138
|
+
- lib/jwt/jwa/eddsa.rb
|
139
|
+
- lib/jwt/jwa/hmac.rb
|
140
|
+
- lib/jwt/jwa/hmac_rbnacl.rb
|
141
|
+
- lib/jwt/jwa/hmac_rbnacl_fixed.rb
|
142
|
+
- lib/jwt/jwa/none.rb
|
143
|
+
- lib/jwt/jwa/ps.rb
|
144
|
+
- lib/jwt/jwa/rsa.rb
|
145
|
+
- lib/jwt/jwa/unsupported.rb
|
146
|
+
- lib/jwt/jwa/wrapper.rb
|
118
147
|
- lib/jwt/jwk.rb
|
119
148
|
- lib/jwt/jwk/ec.rb
|
120
149
|
- lib/jwt/jwk/hmac.rb
|
@@ -134,7 +163,7 @@ licenses:
|
|
134
163
|
- MIT
|
135
164
|
metadata:
|
136
165
|
bug_tracker_uri: https://github.com/jwt/ruby-jwt/issues
|
137
|
-
changelog_uri: https://github.com/jwt/ruby-jwt/blob/v2.
|
166
|
+
changelog_uri: https://github.com/jwt/ruby-jwt/blob/v2.8.1/CHANGELOG.md
|
138
167
|
rubygems_mfa_required: 'true'
|
139
168
|
post_install_message:
|
140
169
|
rdoc_options: []
|
data/lib/jwt/algos/eddsa.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JWT
|
4
|
-
module Algos
|
5
|
-
module Eddsa
|
6
|
-
module_function
|
7
|
-
|
8
|
-
SUPPORTED = %w[ED25519 EdDSA].freeze
|
9
|
-
|
10
|
-
def sign(algorithm, msg, key)
|
11
|
-
if key.class != RbNaCl::Signatures::Ed25519::SigningKey
|
12
|
-
raise EncodeError, "Key given is a #{key.class} but has to be an RbNaCl::Signatures::Ed25519::SigningKey"
|
13
|
-
end
|
14
|
-
unless SUPPORTED.map(&:downcase).map(&:to_sym).include?(algorithm.downcase.to_sym)
|
15
|
-
raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key.primitive} signing key was provided"
|
16
|
-
end
|
17
|
-
|
18
|
-
key.sign(msg)
|
19
|
-
end
|
20
|
-
|
21
|
-
def verify(algorithm, public_key, signing_input, signature)
|
22
|
-
unless SUPPORTED.map(&:downcase).map(&:to_sym).include?(algorithm.downcase.to_sym)
|
23
|
-
raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key.primitive} signing key was provided"
|
24
|
-
end
|
25
|
-
raise DecodeError, "key given is a #{public_key.class} but has to be a RbNaCl::Signatures::Ed25519::VerifyKey" if public_key.class != RbNaCl::Signatures::Ed25519::VerifyKey
|
26
|
-
|
27
|
-
public_key.verify(signature, signing_input)
|
28
|
-
rescue RbNaCl::CryptoError
|
29
|
-
false
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JWT
|
4
|
-
module Algos
|
5
|
-
module HmacRbNaCl
|
6
|
-
module_function
|
7
|
-
|
8
|
-
MAPPING = {
|
9
|
-
'HS256' => ::RbNaCl::HMAC::SHA256,
|
10
|
-
'HS512256' => ::RbNaCl::HMAC::SHA512256,
|
11
|
-
'HS384' => nil,
|
12
|
-
'HS512' => ::RbNaCl::HMAC::SHA512
|
13
|
-
}.freeze
|
14
|
-
|
15
|
-
SUPPORTED = MAPPING.keys
|
16
|
-
|
17
|
-
def sign(algorithm, msg, key)
|
18
|
-
if (hmac = resolve_algorithm(algorithm))
|
19
|
-
hmac.auth(key_for_rbnacl(hmac, key).encode('binary'), msg.encode('binary'))
|
20
|
-
else
|
21
|
-
Hmac.sign(algorithm, msg, key)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def verify(algorithm, key, signing_input, signature)
|
26
|
-
if (hmac = resolve_algorithm(algorithm))
|
27
|
-
hmac.verify(key_for_rbnacl(hmac, key).encode('binary'), signature.encode('binary'), signing_input.encode('binary'))
|
28
|
-
else
|
29
|
-
Hmac.verify(algorithm, key, signing_input, signature)
|
30
|
-
end
|
31
|
-
rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
|
32
|
-
false
|
33
|
-
end
|
34
|
-
|
35
|
-
def key_for_rbnacl(hmac, key)
|
36
|
-
key ||= ''
|
37
|
-
raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
|
38
|
-
|
39
|
-
return padded_empty_key(hmac.key_bytes) if key == ''
|
40
|
-
|
41
|
-
key
|
42
|
-
end
|
43
|
-
|
44
|
-
def resolve_algorithm(algorithm)
|
45
|
-
MAPPING.fetch(algorithm)
|
46
|
-
end
|
47
|
-
|
48
|
-
def padded_empty_key(length)
|
49
|
-
Array.new(length, 0x0).pack('C*').encode('binary')
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JWT
|
4
|
-
module Algos
|
5
|
-
module HmacRbNaClFixed
|
6
|
-
module_function
|
7
|
-
|
8
|
-
MAPPING = {
|
9
|
-
'HS256' => ::RbNaCl::HMAC::SHA256,
|
10
|
-
'HS512256' => ::RbNaCl::HMAC::SHA512256,
|
11
|
-
'HS384' => nil,
|
12
|
-
'HS512' => ::RbNaCl::HMAC::SHA512
|
13
|
-
}.freeze
|
14
|
-
|
15
|
-
SUPPORTED = MAPPING.keys
|
16
|
-
|
17
|
-
def sign(algorithm, msg, key)
|
18
|
-
key ||= ''
|
19
|
-
|
20
|
-
raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
|
21
|
-
|
22
|
-
if (hmac = resolve_algorithm(algorithm)) && key.bytesize <= hmac.key_bytes
|
23
|
-
hmac.auth(padded_key_bytes(key, hmac.key_bytes), msg.encode('binary'))
|
24
|
-
else
|
25
|
-
Hmac.sign(algorithm, msg, key)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def verify(algorithm, key, signing_input, signature)
|
30
|
-
key ||= ''
|
31
|
-
|
32
|
-
raise JWT::DecodeError, 'HMAC key expected to be a String' unless key.is_a?(String)
|
33
|
-
|
34
|
-
if (hmac = resolve_algorithm(algorithm)) && key.bytesize <= hmac.key_bytes
|
35
|
-
hmac.verify(padded_key_bytes(key, hmac.key_bytes), signature.encode('binary'), signing_input.encode('binary'))
|
36
|
-
else
|
37
|
-
Hmac.verify(algorithm, key, signing_input, signature)
|
38
|
-
end
|
39
|
-
rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
|
40
|
-
false
|
41
|
-
end
|
42
|
-
|
43
|
-
def resolve_algorithm(algorithm)
|
44
|
-
MAPPING.fetch(algorithm)
|
45
|
-
end
|
46
|
-
|
47
|
-
def padded_key_bytes(key, bytesize)
|
48
|
-
key.bytes.fill(0, key.bytesize...bytesize).pack('C*')
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
data/lib/jwt/algos.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
begin
|
4
|
-
require 'rbnacl'
|
5
|
-
rescue LoadError
|
6
|
-
raise if defined?(RbNaCl)
|
7
|
-
end
|
8
|
-
require 'openssl'
|
9
|
-
|
10
|
-
require 'jwt/algos/hmac'
|
11
|
-
require 'jwt/algos/eddsa'
|
12
|
-
require 'jwt/algos/ecdsa'
|
13
|
-
require 'jwt/algos/rsa'
|
14
|
-
require 'jwt/algos/ps'
|
15
|
-
require 'jwt/algos/none'
|
16
|
-
require 'jwt/algos/unsupported'
|
17
|
-
require 'jwt/algos/algo_wrapper'
|
18
|
-
|
19
|
-
module JWT
|
20
|
-
module Algos
|
21
|
-
extend self
|
22
|
-
|
23
|
-
ALGOS = [Algos::Ecdsa,
|
24
|
-
Algos::Rsa,
|
25
|
-
Algos::Eddsa,
|
26
|
-
Algos::Ps,
|
27
|
-
Algos::None,
|
28
|
-
Algos::Unsupported].tap do |l|
|
29
|
-
if ::JWT.rbnacl_6_or_greater?
|
30
|
-
require_relative 'algos/hmac_rbnacl'
|
31
|
-
l.unshift(Algos::HmacRbNaCl)
|
32
|
-
elsif ::JWT.rbnacl?
|
33
|
-
require_relative 'algos/hmac_rbnacl_fixed'
|
34
|
-
l.unshift(Algos::HmacRbNaClFixed)
|
35
|
-
else
|
36
|
-
l.unshift(Algos::Hmac)
|
37
|
-
end
|
38
|
-
end.freeze
|
39
|
-
|
40
|
-
def find(algorithm)
|
41
|
-
indexed[algorithm && algorithm.downcase]
|
42
|
-
end
|
43
|
-
|
44
|
-
def create(algorithm)
|
45
|
-
Algos::AlgoWrapper.new(*find(algorithm))
|
46
|
-
end
|
47
|
-
|
48
|
-
def implementation?(algorithm)
|
49
|
-
(algorithm.respond_to?(:valid_alg?) && algorithm.respond_to?(:verify)) ||
|
50
|
-
(algorithm.respond_to?(:alg) && algorithm.respond_to?(:sign))
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def indexed
|
56
|
-
@indexed ||= begin
|
57
|
-
fallback = [nil, Algos::Unsupported]
|
58
|
-
ALGOS.each_with_object(Hash.new(fallback)) do |cls, hash|
|
59
|
-
cls.const_get(:SUPPORTED).each do |alg|
|
60
|
-
hash[alg.downcase] = [alg, cls]
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|