jwt 2.3.0 → 2.7.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.
- checksums.yaml +4 -4
- data/AUTHORS +60 -53
- data/CHANGELOG.md +73 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/CONTRIBUTING.md +99 -0
- data/README.md +188 -40
- data/lib/jwt/algos/algo_wrapper.rb +30 -0
- data/lib/jwt/algos/ecdsa.rb +39 -12
- data/lib/jwt/algos/eddsa.rb +7 -4
- data/lib/jwt/algos/hmac.rb +56 -17
- data/lib/jwt/algos/hmac_rbnacl.rb +53 -0
- data/lib/jwt/algos/hmac_rbnacl_fixed.rb +52 -0
- data/lib/jwt/algos/none.rb +5 -1
- data/lib/jwt/algos/ps.rb +6 -8
- data/lib/jwt/algos/rsa.rb +7 -5
- data/lib/jwt/algos/unsupported.rb +2 -0
- data/lib/jwt/algos.rb +38 -15
- data/lib/jwt/claims_validator.rb +3 -1
- data/lib/jwt/configuration/container.rb +21 -0
- data/lib/jwt/configuration/decode_configuration.rb +46 -0
- data/lib/jwt/configuration/jwk_configuration.rb +27 -0
- data/lib/jwt/configuration.rb +15 -0
- data/lib/jwt/decode.rb +83 -26
- data/lib/jwt/encode.rb +30 -20
- data/lib/jwt/error.rb +1 -0
- data/lib/jwt/jwk/ec.rb +147 -61
- data/lib/jwt/jwk/hmac.rb +69 -24
- data/lib/jwt/jwk/key_base.rb +43 -6
- data/lib/jwt/jwk/key_finder.rb +19 -35
- data/lib/jwt/jwk/kid_as_key_digest.rb +15 -0
- data/lib/jwt/jwk/okp_rbnacl.rb +110 -0
- data/lib/jwt/jwk/rsa.rb +142 -54
- data/lib/jwt/jwk/set.rb +80 -0
- data/lib/jwt/jwk/thumbprint.rb +26 -0
- data/lib/jwt/jwk.rb +15 -11
- data/lib/jwt/security_utils.rb +2 -27
- data/lib/jwt/verify.rb +10 -2
- data/lib/jwt/version.rb +22 -2
- data/lib/jwt/x5c_key_finder.rb +55 -0
- data/lib/jwt.rb +5 -4
- data/ruby-jwt.gemspec +12 -5
- metadata +20 -16
- data/.github/workflows/test.yml +0 -74
- data/.gitignore +0 -11
- data/.rspec +0 -2
- data/.rubocop.yml +0 -97
- data/.rubocop_todo.yml +0 -185
- data/.sourcelevel.yml +0 -18
- data/Appraisals +0 -10
- data/Gemfile +0 -5
- data/Rakefile +0 -14
- data/lib/jwt/default_options.rb +0 -16
- data/lib/jwt/signature.rb +0 -39
|
@@ -0,0 +1,52 @@
|
|
|
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
|
|
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/none.rb
CHANGED
data/lib/jwt/algos/ps.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module JWT
|
|
2
4
|
module Algos
|
|
3
5
|
module Ps
|
|
@@ -7,11 +9,9 @@ module JWT
|
|
|
7
9
|
|
|
8
10
|
SUPPORTED = %w[PS256 PS384 PS512].freeze
|
|
9
11
|
|
|
10
|
-
def sign(
|
|
12
|
+
def sign(algorithm, msg, key)
|
|
11
13
|
require_openssl!
|
|
12
14
|
|
|
13
|
-
algorithm, msg, key = to_sign.values
|
|
14
|
-
|
|
15
15
|
key_class = key.class
|
|
16
16
|
|
|
17
17
|
raise EncodeError, "The given key is a #{key_class}. It has to be an OpenSSL::PKey::RSA instance." if key_class == String
|
|
@@ -21,17 +21,15 @@ module JWT
|
|
|
21
21
|
key.sign_pss(translated_algorithm, msg, salt_length: :digest, mgf1_hash: translated_algorithm)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def verify(
|
|
24
|
+
def verify(algorithm, public_key, signing_input, signature)
|
|
25
25
|
require_openssl!
|
|
26
26
|
|
|
27
|
-
SecurityUtils.verify_ps(
|
|
27
|
+
SecurityUtils.verify_ps(algorithm, public_key, signing_input, signature)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def require_openssl!
|
|
31
31
|
if Object.const_defined?('OpenSSL')
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
unless major.to_i >= 2 && minor.to_i >= 1
|
|
32
|
+
if ::Gem::Version.new(OpenSSL::VERSION) < ::Gem::Version.new('2.1')
|
|
35
33
|
raise JWT::RequiredDependencyError, "You currently have OpenSSL #{OpenSSL::VERSION}. PS support requires >= 2.1"
|
|
36
34
|
end
|
|
37
35
|
else
|
data/lib/jwt/algos/rsa.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module JWT
|
|
2
4
|
module Algos
|
|
3
5
|
module Rsa
|
|
@@ -5,14 +7,14 @@ module JWT
|
|
|
5
7
|
|
|
6
8
|
SUPPORTED = %w[RS256 RS384 RS512].freeze
|
|
7
9
|
|
|
8
|
-
def sign(
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
def sign(algorithm, msg, key)
|
|
11
|
+
raise EncodeError, "The given key is a #{key.class}. It has to be an OpenSSL::PKey::RSA instance." if key.instance_of?(String)
|
|
12
|
+
|
|
11
13
|
key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
|
|
12
14
|
end
|
|
13
15
|
|
|
14
|
-
def verify(
|
|
15
|
-
SecurityUtils.verify_rsa(
|
|
16
|
+
def verify(algorithm, public_key, signing_input, signature)
|
|
17
|
+
SecurityUtils.verify_rsa(algorithm, public_key, signing_input, signature)
|
|
16
18
|
end
|
|
17
19
|
end
|
|
18
20
|
end
|
data/lib/jwt/algos.rb
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
begin
|
|
4
|
+
require 'rbnacl'
|
|
5
|
+
rescue LoadError
|
|
6
|
+
raise if defined?(RbNaCl)
|
|
7
|
+
end
|
|
8
|
+
require 'openssl'
|
|
9
|
+
|
|
10
|
+
require 'jwt/security_utils'
|
|
3
11
|
require 'jwt/algos/hmac'
|
|
4
12
|
require 'jwt/algos/eddsa'
|
|
5
13
|
require 'jwt/algos/ecdsa'
|
|
@@ -7,35 +15,50 @@ require 'jwt/algos/rsa'
|
|
|
7
15
|
require 'jwt/algos/ps'
|
|
8
16
|
require 'jwt/algos/none'
|
|
9
17
|
require 'jwt/algos/unsupported'
|
|
18
|
+
require 'jwt/algos/algo_wrapper'
|
|
10
19
|
|
|
11
|
-
# JWT::Signature module
|
|
12
20
|
module JWT
|
|
13
|
-
# Signature logic for JWT
|
|
14
21
|
module Algos
|
|
15
22
|
extend self
|
|
16
23
|
|
|
17
|
-
ALGOS = [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
ALGOS = [Algos::Ecdsa,
|
|
25
|
+
Algos::Rsa,
|
|
26
|
+
Algos::Eddsa,
|
|
27
|
+
Algos::Ps,
|
|
28
|
+
Algos::None,
|
|
29
|
+
Algos::Unsupported].tap do |l|
|
|
30
|
+
if ::JWT.rbnacl_6_or_greater?
|
|
31
|
+
require_relative 'algos/hmac_rbnacl'
|
|
32
|
+
l.unshift(Algos::HmacRbNaCl)
|
|
33
|
+
elsif ::JWT.rbnacl?
|
|
34
|
+
require_relative 'algos/hmac_rbnacl_fixed'
|
|
35
|
+
l.unshift(Algos::HmacRbNaClFixed)
|
|
36
|
+
else
|
|
37
|
+
l.unshift(Algos::Hmac)
|
|
38
|
+
end
|
|
39
|
+
end.freeze
|
|
26
40
|
|
|
27
41
|
def find(algorithm)
|
|
28
42
|
indexed[algorithm && algorithm.downcase]
|
|
29
43
|
end
|
|
30
44
|
|
|
45
|
+
def create(algorithm)
|
|
46
|
+
Algos::AlgoWrapper.new(*find(algorithm))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def implementation?(algorithm)
|
|
50
|
+
(algorithm.respond_to?(:valid_alg?) && algorithm.respond_to?(:verify)) ||
|
|
51
|
+
(algorithm.respond_to?(:alg) && algorithm.respond_to?(:sign))
|
|
52
|
+
end
|
|
53
|
+
|
|
31
54
|
private
|
|
32
55
|
|
|
33
56
|
def indexed
|
|
34
57
|
@indexed ||= begin
|
|
35
|
-
fallback = [Algos::Unsupported
|
|
36
|
-
ALGOS.each_with_object(Hash.new(fallback)) do |
|
|
37
|
-
|
|
38
|
-
hash[
|
|
58
|
+
fallback = [nil, Algos::Unsupported]
|
|
59
|
+
ALGOS.each_with_object(Hash.new(fallback)) do |cls, hash|
|
|
60
|
+
cls.const_get(:SUPPORTED).each do |alg|
|
|
61
|
+
hash[alg.downcase] = [alg, cls]
|
|
39
62
|
end
|
|
40
63
|
end
|
|
41
64
|
end
|
data/lib/jwt/claims_validator.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative './error'
|
|
2
4
|
|
|
3
5
|
module JWT
|
|
@@ -9,7 +11,7 @@ module JWT
|
|
|
9
11
|
].freeze
|
|
10
12
|
|
|
11
13
|
def initialize(payload)
|
|
12
|
-
@payload = payload.
|
|
14
|
+
@payload = payload.transform_keys(&:to_sym)
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def validate!
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
reset!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def reset!
|
|
16
|
+
@decode = DecodeConfiguration.new
|
|
17
|
+
@jwk = JwkConfiguration.new
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
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
|
data/lib/jwt/decode.rb
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
require 'json'
|
|
4
4
|
|
|
5
|
-
require 'jwt/signature'
|
|
6
5
|
require 'jwt/verify'
|
|
6
|
+
require 'jwt/x5c_key_finder'
|
|
7
|
+
|
|
7
8
|
# JWT::Decode module
|
|
8
9
|
module JWT
|
|
9
10
|
# Decoding logic for JWT
|
|
10
11
|
class Decode
|
|
11
12
|
def initialize(jwt, key, verify, options, &keyfinder)
|
|
12
13
|
raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
|
|
14
|
+
|
|
13
15
|
@jwt = jwt
|
|
14
16
|
@key = key
|
|
15
17
|
@options = options
|
|
@@ -22,51 +24,98 @@ module JWT
|
|
|
22
24
|
def decode_segments
|
|
23
25
|
validate_segment_count!
|
|
24
26
|
if @verify
|
|
25
|
-
|
|
27
|
+
decode_signature
|
|
28
|
+
verify_algo
|
|
29
|
+
set_key
|
|
26
30
|
verify_signature
|
|
27
31
|
verify_claims
|
|
28
32
|
end
|
|
29
33
|
raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
|
|
34
|
+
|
|
30
35
|
[payload, header]
|
|
31
36
|
end
|
|
32
37
|
|
|
33
38
|
private
|
|
34
39
|
|
|
35
40
|
def verify_signature
|
|
41
|
+
return unless @key || @verify
|
|
42
|
+
|
|
43
|
+
return if none_algorithm?
|
|
44
|
+
|
|
45
|
+
raise JWT::DecodeError, 'No verification key available' unless @key
|
|
46
|
+
|
|
47
|
+
return if Array(@key).any? { |key| verify_signature_for?(key) }
|
|
48
|
+
|
|
49
|
+
raise(JWT::VerificationError, 'Signature verification failed')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def verify_algo
|
|
36
53
|
raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') if allowed_algorithms.empty?
|
|
37
|
-
raise(JWT::IncorrectAlgorithm, 'Token is missing alg header') unless
|
|
38
|
-
raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm')
|
|
54
|
+
raise(JWT::IncorrectAlgorithm, 'Token is missing alg header') unless alg_in_header
|
|
55
|
+
raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') if allowed_and_valid_algorithms.empty?
|
|
56
|
+
end
|
|
39
57
|
|
|
58
|
+
def set_key
|
|
40
59
|
@key = find_key(&@keyfinder) if @keyfinder
|
|
41
|
-
@key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks]).key_for(header['kid']) if @options[:jwks]
|
|
60
|
+
@key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks], allow_nil_kid: @options[:allow_nil_kid]).key_for(header['kid']) if @options[:jwks]
|
|
61
|
+
if (x5c_options = @options[:x5c])
|
|
62
|
+
@key = X5cKeyFinder.new(x5c_options[:root_certificates], x5c_options[:crls]).from(header['x5c'])
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def verify_signature_for?(key)
|
|
67
|
+
allowed_and_valid_algorithms.any? do |alg|
|
|
68
|
+
alg.verify(data: signing_input, signature: @signature, verification_key: key)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
42
71
|
|
|
43
|
-
|
|
72
|
+
def allowed_and_valid_algorithms
|
|
73
|
+
@allowed_and_valid_algorithms ||= allowed_algorithms.select { |alg| alg.valid_alg?(alg_in_header) }
|
|
44
74
|
end
|
|
45
75
|
|
|
46
|
-
|
|
47
|
-
|
|
76
|
+
# Order is very important - first check for string keys, next for symbols
|
|
77
|
+
ALGORITHM_KEYS = ['algorithm',
|
|
78
|
+
:algorithm,
|
|
79
|
+
'algorithms',
|
|
80
|
+
:algorithms].freeze
|
|
81
|
+
|
|
82
|
+
def given_algorithms
|
|
83
|
+
ALGORITHM_KEYS.each do |alg_key|
|
|
84
|
+
alg = @options[alg_key]
|
|
85
|
+
return Array(alg) if alg
|
|
86
|
+
end
|
|
87
|
+
[]
|
|
48
88
|
end
|
|
49
89
|
|
|
50
90
|
def allowed_algorithms
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
[]
|
|
91
|
+
@allowed_algorithms ||= resolve_allowed_algorithms
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def resolve_allowed_algorithms
|
|
95
|
+
algs = given_algorithms.map do |alg|
|
|
96
|
+
if Algos.implementation?(alg)
|
|
97
|
+
alg
|
|
98
|
+
else
|
|
99
|
+
Algos.create(alg)
|
|
100
|
+
end
|
|
62
101
|
end
|
|
63
|
-
|
|
102
|
+
|
|
103
|
+
sort_by_alg_header(algs)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Move algorithms matching the JWT alg header to the beginning of the list
|
|
107
|
+
def sort_by_alg_header(algs)
|
|
108
|
+
return algs if algs.size <= 1
|
|
109
|
+
|
|
110
|
+
algs.partition { |alg| alg.valid_alg?(alg_in_header) }.flatten
|
|
64
111
|
end
|
|
65
112
|
|
|
66
113
|
def find_key(&keyfinder)
|
|
67
114
|
key = (keyfinder.arity == 2 ? yield(header, payload) : yield(header))
|
|
68
|
-
|
|
69
|
-
key
|
|
115
|
+
# key can be of type [string, nil, OpenSSL::PKey, Array]
|
|
116
|
+
return key if key && !Array(key).empty?
|
|
117
|
+
|
|
118
|
+
raise JWT::DecodeError, 'No verification key available'
|
|
70
119
|
end
|
|
71
120
|
|
|
72
121
|
def verify_claims
|
|
@@ -77,7 +126,7 @@ module JWT
|
|
|
77
126
|
def validate_segment_count!
|
|
78
127
|
return if segment_length == 3
|
|
79
128
|
return if !@verify && segment_length == 2 # If no verifying required, the signature is not needed
|
|
80
|
-
return if segment_length == 2 &&
|
|
129
|
+
return if segment_length == 2 && none_algorithm?
|
|
81
130
|
|
|
82
131
|
raise(JWT::DecodeError, 'Not enough or too many segments')
|
|
83
132
|
end
|
|
@@ -86,8 +135,16 @@ module JWT
|
|
|
86
135
|
@segments.count
|
|
87
136
|
end
|
|
88
137
|
|
|
89
|
-
def
|
|
90
|
-
|
|
138
|
+
def none_algorithm?
|
|
139
|
+
alg_in_header == 'none'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def decode_signature
|
|
143
|
+
@signature = ::JWT::Base64.url_decode(@segments[2] || '')
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def alg_in_header
|
|
147
|
+
header['alg']
|
|
91
148
|
end
|
|
92
149
|
|
|
93
150
|
def header
|
|
@@ -103,7 +160,7 @@ module JWT
|
|
|
103
160
|
end
|
|
104
161
|
|
|
105
162
|
def parse_and_decode(segment)
|
|
106
|
-
JWT::JSON.parse(JWT::Base64.url_decode(segment))
|
|
163
|
+
JWT::JSON.parse(::JWT::Base64.url_decode(segment))
|
|
107
164
|
rescue ::JSON::ParserError
|
|
108
165
|
raise JWT::DecodeError, 'Invalid segment encoding'
|
|
109
166
|
end
|
data/lib/jwt/encode.rb
CHANGED
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative '
|
|
4
|
-
require_relative '
|
|
3
|
+
require_relative 'algos'
|
|
4
|
+
require_relative 'claims_validator'
|
|
5
5
|
|
|
6
6
|
# JWT::Encode module
|
|
7
7
|
module JWT
|
|
8
8
|
# Encoding logic for JWT
|
|
9
9
|
class Encode
|
|
10
|
-
|
|
11
|
-
ALG_KEY = 'alg'.freeze
|
|
10
|
+
ALG_KEY = 'alg'
|
|
12
11
|
|
|
13
12
|
def initialize(options)
|
|
14
|
-
@payload
|
|
15
|
-
@key
|
|
16
|
-
|
|
17
|
-
@headers
|
|
13
|
+
@payload = options[:payload]
|
|
14
|
+
@key = options[:key]
|
|
15
|
+
@algorithm = resolve_algorithm(options[:algorithm])
|
|
16
|
+
@headers = options[:headers].transform_keys(&:to_s)
|
|
17
|
+
@headers[ALG_KEY] = @algorithm.alg
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def segments
|
|
21
|
-
|
|
21
|
+
validate_claims!
|
|
22
|
+
combine(encoded_header_and_payload, encoded_signature)
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
private
|
|
25
26
|
|
|
27
|
+
def resolve_algorithm(algorithm)
|
|
28
|
+
return algorithm if Algos.implementation?(algorithm)
|
|
29
|
+
|
|
30
|
+
Algos.create(algorithm)
|
|
31
|
+
end
|
|
32
|
+
|
|
26
33
|
def encoded_header
|
|
27
34
|
@encoded_header ||= encode_header
|
|
28
35
|
end
|
|
@@ -40,26 +47,29 @@ module JWT
|
|
|
40
47
|
end
|
|
41
48
|
|
|
42
49
|
def encode_header
|
|
43
|
-
@headers
|
|
44
|
-
encode(@headers)
|
|
50
|
+
encode_data(@headers)
|
|
45
51
|
end
|
|
46
52
|
|
|
47
53
|
def encode_payload
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
end
|
|
54
|
+
encode_data(@payload)
|
|
55
|
+
end
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
def signature
|
|
58
|
+
@algorithm.sign(data: encoded_header_and_payload, signing_key: @key)
|
|
53
59
|
end
|
|
54
60
|
|
|
55
|
-
def
|
|
56
|
-
return
|
|
61
|
+
def validate_claims!
|
|
62
|
+
return unless @payload.is_a?(Hash)
|
|
63
|
+
|
|
64
|
+
ClaimsValidator.new(@payload).validate!
|
|
65
|
+
end
|
|
57
66
|
|
|
58
|
-
|
|
67
|
+
def encode_signature
|
|
68
|
+
::JWT::Base64.url_encode(signature)
|
|
59
69
|
end
|
|
60
70
|
|
|
61
|
-
def
|
|
62
|
-
JWT::Base64.url_encode(JWT::JSON.generate(data))
|
|
71
|
+
def encode_data(data)
|
|
72
|
+
::JWT::Base64.url_encode(JWT::JSON.generate(data))
|
|
63
73
|
end
|
|
64
74
|
|
|
65
75
|
def combine(*parts)
|
data/lib/jwt/error.rb
CHANGED
|
@@ -10,6 +10,7 @@ module JWT
|
|
|
10
10
|
class IncorrectAlgorithm < DecodeError; end
|
|
11
11
|
class ImmatureSignature < DecodeError; end
|
|
12
12
|
class InvalidIssuerError < DecodeError; end
|
|
13
|
+
class UnsupportedEcdsaCurve < IncorrectAlgorithm; end
|
|
13
14
|
class InvalidIatError < DecodeError; end
|
|
14
15
|
class InvalidAudError < DecodeError; end
|
|
15
16
|
class InvalidSubError < DecodeError; end
|