jwt 2.8.2 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,49 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
4
+ module JWA
5
+ class HmacRbNaCl
6
+ include JWT::JWA::SigningAlgorithm
7
+
8
+ def initialize(alg, hmac)
9
+ @alg = alg
10
+ @hmac = hmac
11
+ end
12
+
13
+ def sign(data:, signing_key:)
14
+ Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
15
+ hmac.auth(key_for_rbnacl(hmac, signing_key).encode('binary'), data.encode('binary'))
16
+ end
17
+
18
+ def verify(data:, signature:, verification_key:)
19
+ Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
20
+ hmac.verify(key_for_rbnacl(hmac, verification_key).encode('binary'), signature.encode('binary'), data.encode('binary'))
21
+ rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
22
+ false
23
+ end
24
+
25
+ register_algorithm(new('HS512256', ::RbNaCl::HMAC::SHA512256))
26
+
27
+ private
28
+
29
+ attr_reader :hmac
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 padded_empty_key(length)
41
+ Array.new(length, 0x0).pack('C*').encode('binary')
47
42
  end
48
43
  end
49
44
  end
@@ -1,45 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
4
+ module JWA
5
+ class HmacRbNaClFixed
6
+ include JWT::JWA::SigningAlgorithm
7
+
8
+ def initialize(alg, hmac)
9
+ @alg = alg
10
+ @hmac = hmac
11
+ end
12
+
13
+ def sign(data:, signing_key:)
14
+ signing_key ||= ''
15
+ Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
16
+ raise JWT::DecodeError, 'HMAC key expected to be a String' unless signing_key.is_a?(String)
17
+
18
+ hmac.auth(padded_key_bytes(signing_key, hmac.key_bytes), data.encode('binary'))
19
+ end
20
+
21
+ def verify(data:, signature:, verification_key:)
22
+ verification_key ||= ''
23
+ Deprecations.warning("The use of the algorithm #{alg} is deprecated and will be removed in the next major version of ruby-jwt")
24
+ raise JWT::DecodeError, 'HMAC key expected to be a String' unless verification_key.is_a?(String)
25
+
26
+ hmac.verify(padded_key_bytes(verification_key, hmac.key_bytes), signature.encode('binary'), data.encode('binary'))
27
+ rescue ::RbNaCl::BadAuthenticatorError, ::RbNaCl::LengthError
28
+ false
29
+ end
30
+
31
+ register_algorithm(new('HS512256', ::RbNaCl::HMAC::SHA512256))
32
+
33
+ private
34
+
35
+ attr_reader :hmac
36
+
37
+ def padded_key_bytes(key, bytesize)
38
+ key.bytes.fill(0, key.bytesize...bytesize).pack('C*')
43
39
  end
44
40
  end
45
41
  end
data/lib/jwt/jwa/none.rb CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  module JWT
4
4
  module JWA
5
- module None
6
- module_function
5
+ class None
6
+ include JWT::JWA::SigningAlgorithm
7
7
 
8
- SUPPORTED = %w[none].freeze
8
+ def initialize
9
+ @alg = 'none'
10
+ end
9
11
 
10
12
  def sign(*)
11
13
  ''
@@ -14,6 +16,8 @@ module JWT
14
16
  def verify(*)
15
17
  true
16
18
  end
19
+
20
+ register_algorithm(new)
17
21
  end
18
22
  end
19
23
  end
data/lib/jwt/jwa/ps.rb CHANGED
@@ -2,29 +2,35 @@
2
2
 
3
3
  module JWT
4
4
  module JWA
5
- module Ps
6
- # RSASSA-PSS signing algorithms
5
+ class Ps
6
+ include JWT::JWA::SigningAlgorithm
7
7
 
8
- module_function
9
-
10
- SUPPORTED = %w[PS256 PS384 PS512].freeze
8
+ def initialize(alg)
9
+ @alg = alg
10
+ @digest_algorithm = alg.sub('PS', 'sha')
11
+ end
11
12
 
12
- def sign(algorithm, msg, key)
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."
13
+ def sign(data:, signing_key:)
14
+ unless signing_key.is_a?(::OpenSSL::PKey::RSA)
15
+ raise_sign_error!("The given key is a #{signing_key.class}. It has to be an OpenSSL::PKey::RSA instance.")
15
16
  end
16
17
 
17
- translated_algorithm = algorithm.sub('PS', 'sha')
18
-
19
- key.sign_pss(translated_algorithm, msg, salt_length: :digest, mgf1_hash: translated_algorithm)
18
+ signing_key.sign_pss(digest_algorithm, data, salt_length: :digest, mgf1_hash: digest_algorithm)
20
19
  end
21
20
 
22
- def verify(algorithm, public_key, signing_input, signature)
23
- translated_algorithm = algorithm.sub('PS', 'sha')
24
- public_key.verify_pss(translated_algorithm, signature, signing_input, salt_length: :auto, mgf1_hash: translated_algorithm)
21
+ def verify(data:, signature:, verification_key:)
22
+ verification_key.verify_pss(digest_algorithm, signature, data, salt_length: :auto, mgf1_hash: digest_algorithm)
25
23
  rescue OpenSSL::PKey::PKeyError
26
24
  raise JWT::VerificationError, 'Signature verification raised'
27
25
  end
26
+
27
+ register_algorithm(new('PS256'))
28
+ register_algorithm(new('PS384'))
29
+ register_algorithm(new('PS512'))
30
+
31
+ private
32
+
33
+ attr_reader :digest_algorithm
28
34
  end
29
35
  end
30
36
  end
data/lib/jwt/jwa/rsa.rb CHANGED
@@ -2,24 +2,35 @@
2
2
 
3
3
  module JWT
4
4
  module JWA
5
- module Rsa
6
- module_function
5
+ class Rsa
6
+ include JWT::JWA::SigningAlgorithm
7
7
 
8
- SUPPORTED = %w[RS256 RS384 RS512].freeze
8
+ def initialize(alg)
9
+ @alg = alg
10
+ @digest = OpenSSL::Digest.new(alg.sub('RS', 'SHA'))
11
+ end
9
12
 
10
- def sign(algorithm, msg, key)
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
+ def sign(data:, signing_key:)
14
+ unless signing_key.is_a?(OpenSSL::PKey::RSA)
15
+ raise_sign_error!("The given key is a #{signing_key.class}. It has to be an OpenSSL::PKey::RSA instance")
13
16
  end
14
17
 
15
- key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
18
+ signing_key.sign(digest, data)
16
19
  end
17
20
 
18
- def verify(algorithm, public_key, signing_input, signature)
19
- public_key.verify(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
21
+ def verify(data:, signature:, verification_key:)
22
+ verification_key.verify(digest, signature, data)
20
23
  rescue OpenSSL::PKey::PKeyError
21
24
  raise JWT::VerificationError, 'Signature verification raised'
22
25
  end
26
+
27
+ register_algorithm(new('RS256'))
28
+ register_algorithm(new('RS384'))
29
+ register_algorithm(new('RS512'))
30
+
31
+ private
32
+
33
+ attr_reader :digest
23
34
  end
24
35
  end
25
36
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module JWA
5
+ module SigningAlgorithm
6
+ module ClassMethods
7
+ def register_algorithm(algo)
8
+ ::JWT::JWA.register_algorithm(algo)
9
+ end
10
+ end
11
+
12
+ def self.included(klass)
13
+ klass.extend(ClassMethods)
14
+ end
15
+
16
+ attr_reader :alg
17
+
18
+ def valid_alg?(alg_to_check)
19
+ alg&.casecmp(alg_to_check)&.zero? == true
20
+ end
21
+
22
+ def header(*)
23
+ { 'alg' => alg }
24
+ end
25
+
26
+ def sign(*)
27
+ raise_sign_error!('Algorithm implementation is missing the sign method')
28
+ end
29
+
30
+ def verify(*)
31
+ raise_verify_error!('Algorithm implementation is missing the verify method')
32
+ end
33
+
34
+ def raise_verify_error!(message)
35
+ raise(DecodeError.new(message).tap { |e| e.set_backtrace(caller(1)) })
36
+ end
37
+
38
+ def raise_sign_error!(message)
39
+ raise(EncodeError.new(message).tap { |e| e.set_backtrace(caller(1)) })
40
+ end
41
+ end
42
+
43
+ class << self
44
+ def register_algorithm(algo)
45
+ algorithms[algo.alg.to_s.downcase] = algo
46
+ end
47
+
48
+ def find(algo)
49
+ algorithms.fetch(algo.to_s.downcase, Unsupported)
50
+ end
51
+
52
+ private
53
+
54
+ def algorithms
55
+ @algorithms ||= {}
56
+ end
57
+ end
58
+ end
59
+ end
@@ -3,16 +3,16 @@
3
3
  module JWT
4
4
  module JWA
5
5
  module Unsupported
6
- module_function
6
+ class << self
7
+ include JWT::JWA::SigningAlgorithm
7
8
 
8
- SUPPORTED = [].freeze
9
+ def sign(*)
10
+ raise_sign_error!('Unsupported signing method')
11
+ end
9
12
 
10
- def sign(*)
11
- raise NotImplementedError, 'Unsupported signing method'
12
- end
13
-
14
- def verify(*)
15
- raise JWT::VerificationError, 'Algorithm not supported'
13
+ def verify(*)
14
+ raise JWT::VerificationError, 'Algorithm not supported'
15
+ end
16
16
  end
17
17
  end
18
18
  end
@@ -3,23 +3,40 @@
3
3
  module JWT
4
4
  module JWA
5
5
  class Wrapper
6
- attr_reader :alg, :cls
6
+ include SigningAlgorithm
7
7
 
8
- def initialize(alg, cls)
9
- @alg = alg
10
- @cls = cls
8
+ def initialize(algorithm)
9
+ @algorithm = algorithm
10
+ end
11
+
12
+ def alg
13
+ return @algorithm.alg if @algorithm.respond_to?(:alg)
14
+
15
+ super
11
16
  end
12
17
 
13
18
  def valid_alg?(alg_to_check)
14
- alg&.casecmp(alg_to_check)&.zero? == true
19
+ return @algorithm.valid_alg?(alg_to_check) if @algorithm.respond_to?(:valid_alg?)
20
+
21
+ super
15
22
  end
16
23
 
17
- def sign(data:, signing_key:)
18
- cls.sign(alg, data, signing_key)
24
+ def header(*args, **kwargs)
25
+ return @algorithm.header(*args, **kwargs) if @algorithm.respond_to?(:header)
26
+
27
+ super
19
28
  end
20
29
 
21
- def verify(data:, signature:, verification_key:)
22
- cls.verify(alg, verification_key, data, signature)
30
+ def sign(*args, **kwargs)
31
+ return @algorithm.sign(*args, **kwargs) if @algorithm.respond_to?(:sign)
32
+
33
+ super
34
+ end
35
+
36
+ def verify(*args, **kwargs)
37
+ return @algorithm.verify(*args, **kwargs) if @algorithm.respond_to?(:verify)
38
+
39
+ super
23
40
  end
24
41
  end
25
42
  end
data/lib/jwt/jwa.rb CHANGED
@@ -8,54 +8,37 @@ rescue LoadError
8
8
  raise if defined?(RbNaCl)
9
9
  end
10
10
 
11
- require_relative 'jwa/hmac'
12
- require_relative 'jwa/eddsa'
11
+ require_relative 'jwa/signing_algorithm'
13
12
  require_relative 'jwa/ecdsa'
14
- require_relative 'jwa/rsa'
15
- require_relative 'jwa/ps'
13
+ require_relative 'jwa/hmac'
16
14
  require_relative 'jwa/none'
15
+ require_relative 'jwa/ps'
16
+ require_relative 'jwa/rsa'
17
17
  require_relative 'jwa/unsupported'
18
18
  require_relative 'jwa/wrapper'
19
19
 
20
+ if JWT.rbnacl?
21
+ require_relative 'jwa/eddsa'
22
+ end
23
+
24
+ if JWT.rbnacl_6_or_greater?
25
+ require_relative 'jwa/hmac_rbnacl'
26
+ elsif JWT.rbnacl?
27
+ require_relative 'jwa/hmac_rbnacl_fixed'
28
+ end
29
+
20
30
  module JWT
21
31
  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
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
33
+ def resolve(algorithm)
34
+ return find(algorithm) if algorithm.is_a?(String) || algorithm.is_a?(Symbol)
47
35
 
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
36
+ unless algorithm.is_a?(SigningAlgorithm)
37
+ Deprecations.warning('Custom algorithms are required to include JWT::JWA::SigningAlgorithm')
38
+ return Wrapper.new(algorithm)
58
39
  end
40
+
41
+ algorithm
59
42
  end
60
43
  end
61
44
  end
data/lib/jwt/version.rb CHANGED
@@ -11,9 +11,9 @@ module JWT
11
11
  # major version
12
12
  MAJOR = 2
13
13
  # minor version
14
- MINOR = 8
14
+ MINOR = 9
15
15
  # tiny version
16
- TINY = 2
16
+ TINY = 0
17
17
  # alpha, beta, etc. tag
18
18
  PRE = nil
19
19
 
data/lib/jwt.rb CHANGED
@@ -9,6 +9,7 @@ require 'jwt/deprecations'
9
9
  require 'jwt/encode'
10
10
  require 'jwt/error'
11
11
  require 'jwt/jwk'
12
+ require 'jwt/claims'
12
13
 
13
14
  # JSON Web Token implementation
14
15
  #
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jwt
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.2
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Rudat
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-18 00:00:00.000000000 Z
11
+ date: 2024-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -123,7 +123,16 @@ files:
123
123
  - README.md
124
124
  - lib/jwt.rb
125
125
  - lib/jwt/base64.rb
126
- - lib/jwt/claims_validator.rb
126
+ - lib/jwt/claims.rb
127
+ - lib/jwt/claims/audience.rb
128
+ - lib/jwt/claims/expiration.rb
129
+ - lib/jwt/claims/issued_at.rb
130
+ - lib/jwt/claims/issuer.rb
131
+ - lib/jwt/claims/jwt_id.rb
132
+ - lib/jwt/claims/not_before.rb
133
+ - lib/jwt/claims/numeric.rb
134
+ - lib/jwt/claims/required.rb
135
+ - lib/jwt/claims/subject.rb
127
136
  - lib/jwt/configuration.rb
128
137
  - lib/jwt/configuration/container.rb
129
138
  - lib/jwt/configuration/decode_configuration.rb
@@ -142,6 +151,7 @@ files:
142
151
  - lib/jwt/jwa/none.rb
143
152
  - lib/jwt/jwa/ps.rb
144
153
  - lib/jwt/jwa/rsa.rb
154
+ - lib/jwt/jwa/signing_algorithm.rb
145
155
  - lib/jwt/jwa/unsupported.rb
146
156
  - lib/jwt/jwa/wrapper.rb
147
157
  - lib/jwt/jwk.rb
@@ -154,7 +164,6 @@ files:
154
164
  - lib/jwt/jwk/rsa.rb
155
165
  - lib/jwt/jwk/set.rb
156
166
  - lib/jwt/jwk/thumbprint.rb
157
- - lib/jwt/verify.rb
158
167
  - lib/jwt/version.rb
159
168
  - lib/jwt/x5c_key_finder.rb
160
169
  - ruby-jwt.gemspec
@@ -163,9 +172,9 @@ licenses:
163
172
  - MIT
164
173
  metadata:
165
174
  bug_tracker_uri: https://github.com/jwt/ruby-jwt/issues
166
- changelog_uri: https://github.com/jwt/ruby-jwt/blob/v2.8.2/CHANGELOG.md
175
+ changelog_uri: https://github.com/jwt/ruby-jwt/blob/v2.9.0/CHANGELOG.md
167
176
  rubygems_mfa_required: 'true'
168
- post_install_message:
177
+ post_install_message:
169
178
  rdoc_options: []
170
179
  require_paths:
171
180
  - lib
@@ -180,8 +189,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
180
189
  - !ruby/object:Gem::Version
181
190
  version: '0'
182
191
  requirements: []
183
- rubygems_version: 3.5.3
184
- signing_key:
192
+ rubygems_version: 3.5.16
193
+ signing_key:
185
194
  specification_version: 4
186
195
  summary: JSON Web Token implementation in Ruby
187
196
  test_files: []
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'error'
4
-
5
- module JWT
6
- class ClaimsValidator
7
- NUMERIC_CLAIMS = %i[
8
- exp
9
- iat
10
- nbf
11
- ].freeze
12
-
13
- def initialize(payload)
14
- @payload = payload.transform_keys(&:to_sym)
15
- end
16
-
17
- def validate!
18
- validate_numeric_claims
19
-
20
- true
21
- end
22
-
23
- private
24
-
25
- def validate_numeric_claims
26
- NUMERIC_CLAIMS.each do |claim|
27
- validate_is_numeric(claim) if @payload.key?(claim)
28
- end
29
- end
30
-
31
- def validate_is_numeric(claim)
32
- return if @payload[claim].is_a?(Numeric)
33
-
34
- raise InvalidPayload, "#{claim} claim must be a Numeric value but it is a #{@payload[claim].class}"
35
- end
36
- end
37
- end