jwt 2.7.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +84 -27
- data/README.md +37 -19
- data/lib/jwt/base64.rb +16 -2
- data/lib/jwt/claims/audience.rb +20 -0
- data/lib/jwt/claims/expiration.rb +22 -0
- data/lib/jwt/claims/issued_at.rb +15 -0
- data/lib/jwt/claims/issuer.rb +24 -0
- data/lib/jwt/claims/jwt_id.rb +25 -0
- data/lib/jwt/claims/not_before.rb +22 -0
- data/lib/jwt/claims/numeric.rb +43 -0
- data/lib/jwt/claims/required.rb +23 -0
- data/lib/jwt/claims/subject.rb +20 -0
- data/lib/jwt/claims.rb +38 -0
- data/lib/jwt/configuration/container.rb +14 -3
- data/lib/jwt/configuration/jwk_configuration.rb +1 -1
- data/lib/jwt/decode.rb +12 -21
- data/lib/jwt/deprecations.rb +48 -0
- data/lib/jwt/encode.rb +4 -14
- data/lib/jwt/error.rb +1 -0
- data/lib/jwt/{algos → jwa}/ecdsa.rb +39 -26
- data/lib/jwt/jwa/eddsa.rb +34 -0
- data/lib/jwt/{algos → jwa}/hmac.rb +25 -19
- data/lib/jwt/jwa/hmac_rbnacl.rb +45 -0
- data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +42 -0
- data/lib/jwt/jwa/none.rb +23 -0
- data/lib/jwt/jwa/ps.rb +36 -0
- data/lib/jwt/jwa/rsa.rb +36 -0
- data/lib/jwt/jwa/signing_algorithm.rb +59 -0
- data/lib/jwt/jwa/unsupported.rb +19 -0
- data/lib/jwt/jwa/wrapper.rb +43 -0
- data/lib/jwt/jwa.rb +45 -0
- data/lib/jwt/jwk/ec.rb +42 -27
- data/lib/jwt/jwk/key_base.rb +3 -1
- data/lib/jwt/jwk/key_finder.rb +4 -4
- data/lib/jwt/jwk/set.rb +1 -1
- data/lib/jwt/jwk.rb +1 -1
- data/lib/jwt/version.rb +4 -3
- data/lib/jwt/x5c_key_finder.rb +2 -5
- data/lib/jwt.rb +5 -1
- data/ruby-jwt.gemspec +3 -0
- metadata +58 -20
- data/lib/jwt/algos/algo_wrapper.rb +0 -26
- 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/none.rb +0 -19
- data/lib/jwt/algos/ps.rb +0 -41
- data/lib/jwt/algos/rsa.rb +0 -23
- data/lib/jwt/algos/unsupported.rb +0 -19
- data/lib/jwt/algos.rb +0 -66
- data/lib/jwt/claims_validator.rb +0 -37
- data/lib/jwt/verify.rb +0 -113
data/lib/jwt/algos/ps.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JWT
|
4
|
-
module Algos
|
5
|
-
module Ps
|
6
|
-
# RSASSA-PSS signing algorithms
|
7
|
-
|
8
|
-
module_function
|
9
|
-
|
10
|
-
SUPPORTED = %w[PS256 PS384 PS512].freeze
|
11
|
-
|
12
|
-
def sign(algorithm, msg, key)
|
13
|
-
require_openssl!
|
14
|
-
|
15
|
-
raise EncodeError, "The given key is a #{key_class}. It has to be an OpenSSL::PKey::RSA instance." if key.is_a?(String)
|
16
|
-
|
17
|
-
translated_algorithm = algorithm.sub('PS', 'sha')
|
18
|
-
|
19
|
-
key.sign_pss(translated_algorithm, msg, salt_length: :digest, mgf1_hash: translated_algorithm)
|
20
|
-
end
|
21
|
-
|
22
|
-
def verify(algorithm, public_key, signing_input, signature)
|
23
|
-
require_openssl!
|
24
|
-
translated_algorithm = algorithm.sub('PS', 'sha')
|
25
|
-
public_key.verify_pss(translated_algorithm, signature, signing_input, salt_length: :auto, mgf1_hash: translated_algorithm)
|
26
|
-
rescue OpenSSL::PKey::PKeyError
|
27
|
-
raise JWT::VerificationError, 'Signature verification raised'
|
28
|
-
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
|
-
end
|
40
|
-
end
|
41
|
-
end
|
data/lib/jwt/algos/rsa.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JWT
|
4
|
-
module Algos
|
5
|
-
module Rsa
|
6
|
-
module_function
|
7
|
-
|
8
|
-
SUPPORTED = %w[RS256 RS384 RS512].freeze
|
9
|
-
|
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
|
-
|
13
|
-
key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
|
14
|
-
end
|
15
|
-
|
16
|
-
def verify(algorithm, public_key, signing_input, signature)
|
17
|
-
public_key.verify(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
|
18
|
-
rescue OpenSSL::PKey::PKeyError
|
19
|
-
raise JWT::VerificationError, 'Signature verification raised'
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JWT
|
4
|
-
module Algos
|
5
|
-
module Unsupported
|
6
|
-
module_function
|
7
|
-
|
8
|
-
SUPPORTED = [].freeze
|
9
|
-
|
10
|
-
def sign(*)
|
11
|
-
raise NotImplementedError, 'Unsupported signing method'
|
12
|
-
end
|
13
|
-
|
14
|
-
def verify(*)
|
15
|
-
raise JWT::VerificationError, 'Algorithm not supported'
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
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
|
data/lib/jwt/claims_validator.rb
DELETED
@@ -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
|
data/lib/jwt/verify.rb
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'jwt/error'
|
4
|
-
|
5
|
-
module JWT
|
6
|
-
# JWT verify methods
|
7
|
-
class Verify
|
8
|
-
DEFAULTS = {
|
9
|
-
leeway: 0
|
10
|
-
}.freeze
|
11
|
-
|
12
|
-
class << self
|
13
|
-
%w[verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub verify_required_claims].each do |method_name|
|
14
|
-
define_method method_name do |payload, options|
|
15
|
-
new(payload, options).send(method_name)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def verify_claims(payload, options)
|
20
|
-
options.each do |key, val|
|
21
|
-
next unless key.to_s =~ /verify/
|
22
|
-
|
23
|
-
Verify.send(key, payload, options) if val
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def initialize(payload, options)
|
29
|
-
@payload = payload
|
30
|
-
@options = DEFAULTS.merge(options)
|
31
|
-
end
|
32
|
-
|
33
|
-
def verify_aud
|
34
|
-
return unless (options_aud = @options[:aud])
|
35
|
-
|
36
|
-
aud = @payload['aud']
|
37
|
-
raise(JWT::InvalidAudError, "Invalid audience. Expected #{options_aud}, received #{aud || '<none>'}") if ([*aud] & [*options_aud]).empty?
|
38
|
-
end
|
39
|
-
|
40
|
-
def verify_expiration
|
41
|
-
return unless @payload.include?('exp')
|
42
|
-
raise(JWT::ExpiredSignature, 'Signature has expired') if @payload['exp'].to_i <= (Time.now.to_i - exp_leeway)
|
43
|
-
end
|
44
|
-
|
45
|
-
def verify_iat
|
46
|
-
return unless @payload.include?('iat')
|
47
|
-
|
48
|
-
iat = @payload['iat']
|
49
|
-
raise(JWT::InvalidIatError, 'Invalid iat') if !iat.is_a?(Numeric) || iat.to_f > Time.now.to_f
|
50
|
-
end
|
51
|
-
|
52
|
-
def verify_iss
|
53
|
-
return unless (options_iss = @options[:iss])
|
54
|
-
|
55
|
-
iss = @payload['iss']
|
56
|
-
|
57
|
-
options_iss = Array(options_iss).map { |item| item.is_a?(Symbol) ? item.to_s : item }
|
58
|
-
|
59
|
-
case iss
|
60
|
-
when *options_iss
|
61
|
-
nil
|
62
|
-
else
|
63
|
-
raise(JWT::InvalidIssuerError, "Invalid issuer. Expected #{options_iss}, received #{iss || '<none>'}")
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def verify_jti
|
68
|
-
options_verify_jti = @options[:verify_jti]
|
69
|
-
jti = @payload['jti']
|
70
|
-
|
71
|
-
if options_verify_jti.respond_to?(:call)
|
72
|
-
verified = options_verify_jti.arity == 2 ? options_verify_jti.call(jti, @payload) : options_verify_jti.call(jti)
|
73
|
-
raise(JWT::InvalidJtiError, 'Invalid jti') unless verified
|
74
|
-
elsif jti.to_s.strip.empty?
|
75
|
-
raise(JWT::InvalidJtiError, 'Missing jti')
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def verify_not_before
|
80
|
-
return unless @payload.include?('nbf')
|
81
|
-
raise(JWT::ImmatureSignature, 'Signature nbf has not been reached') if @payload['nbf'].to_i > (Time.now.to_i + nbf_leeway)
|
82
|
-
end
|
83
|
-
|
84
|
-
def verify_sub
|
85
|
-
return unless (options_sub = @options[:sub])
|
86
|
-
|
87
|
-
sub = @payload['sub']
|
88
|
-
raise(JWT::InvalidSubError, "Invalid subject. Expected #{options_sub}, received #{sub || '<none>'}") unless sub.to_s == options_sub.to_s
|
89
|
-
end
|
90
|
-
|
91
|
-
def verify_required_claims
|
92
|
-
return unless (options_required_claims = @options[:required_claims])
|
93
|
-
|
94
|
-
options_required_claims.each do |required_claim|
|
95
|
-
raise(JWT::MissingRequiredClaim, "Missing required claim #{required_claim}") unless @payload.include?(required_claim)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
private
|
100
|
-
|
101
|
-
def global_leeway
|
102
|
-
@options[:leeway]
|
103
|
-
end
|
104
|
-
|
105
|
-
def exp_leeway
|
106
|
-
@options[:exp_leeway] || global_leeway
|
107
|
-
end
|
108
|
-
|
109
|
-
def nbf_leeway
|
110
|
-
@options[:nbf_leeway] || global_leeway
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|