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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -27
  3. data/README.md +37 -19
  4. data/lib/jwt/base64.rb +16 -2
  5. data/lib/jwt/claims/audience.rb +20 -0
  6. data/lib/jwt/claims/expiration.rb +22 -0
  7. data/lib/jwt/claims/issued_at.rb +15 -0
  8. data/lib/jwt/claims/issuer.rb +24 -0
  9. data/lib/jwt/claims/jwt_id.rb +25 -0
  10. data/lib/jwt/claims/not_before.rb +22 -0
  11. data/lib/jwt/claims/numeric.rb +43 -0
  12. data/lib/jwt/claims/required.rb +23 -0
  13. data/lib/jwt/claims/subject.rb +20 -0
  14. data/lib/jwt/claims.rb +38 -0
  15. data/lib/jwt/configuration/container.rb +14 -3
  16. data/lib/jwt/configuration/jwk_configuration.rb +1 -1
  17. data/lib/jwt/decode.rb +12 -21
  18. data/lib/jwt/deprecations.rb +48 -0
  19. data/lib/jwt/encode.rb +4 -14
  20. data/lib/jwt/error.rb +1 -0
  21. data/lib/jwt/{algos → jwa}/ecdsa.rb +39 -26
  22. data/lib/jwt/jwa/eddsa.rb +34 -0
  23. data/lib/jwt/{algos → jwa}/hmac.rb +25 -19
  24. data/lib/jwt/jwa/hmac_rbnacl.rb +45 -0
  25. data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +42 -0
  26. data/lib/jwt/jwa/none.rb +23 -0
  27. data/lib/jwt/jwa/ps.rb +36 -0
  28. data/lib/jwt/jwa/rsa.rb +36 -0
  29. data/lib/jwt/jwa/signing_algorithm.rb +59 -0
  30. data/lib/jwt/jwa/unsupported.rb +19 -0
  31. data/lib/jwt/jwa/wrapper.rb +43 -0
  32. data/lib/jwt/jwa.rb +45 -0
  33. data/lib/jwt/jwk/ec.rb +42 -27
  34. data/lib/jwt/jwk/key_base.rb +3 -1
  35. data/lib/jwt/jwk/key_finder.rb +4 -4
  36. data/lib/jwt/jwk/set.rb +1 -1
  37. data/lib/jwt/jwk.rb +1 -1
  38. data/lib/jwt/version.rb +4 -3
  39. data/lib/jwt/x5c_key_finder.rb +2 -5
  40. data/lib/jwt.rb +5 -1
  41. data/ruby-jwt.gemspec +3 -0
  42. metadata +58 -20
  43. data/lib/jwt/algos/algo_wrapper.rb +0 -26
  44. data/lib/jwt/algos/eddsa.rb +0 -33
  45. data/lib/jwt/algos/hmac_rbnacl.rb +0 -53
  46. data/lib/jwt/algos/hmac_rbnacl_fixed.rb +0 -52
  47. data/lib/jwt/algos/none.rb +0 -19
  48. data/lib/jwt/algos/ps.rb +0 -41
  49. data/lib/jwt/algos/rsa.rb +0 -23
  50. data/lib/jwt/algos/unsupported.rb +0 -19
  51. data/lib/jwt/algos.rb +0 -66
  52. data/lib/jwt/claims_validator.rb +0 -37
  53. 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
@@ -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