jwt 2.8.2 → 3.1.1

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +149 -31
  3. data/CODE_OF_CONDUCT.md +14 -14
  4. data/CONTRIBUTING.md +9 -10
  5. data/README.md +299 -234
  6. data/UPGRADING.md +47 -0
  7. data/lib/jwt/base64.rb +4 -10
  8. data/lib/jwt/claims/audience.rb +30 -0
  9. data/lib/jwt/claims/crit.rb +35 -0
  10. data/lib/jwt/claims/decode_verifier.rb +40 -0
  11. data/lib/jwt/claims/expiration.rb +32 -0
  12. data/lib/jwt/claims/issued_at.rb +22 -0
  13. data/lib/jwt/claims/issuer.rb +34 -0
  14. data/lib/jwt/claims/jwt_id.rb +35 -0
  15. data/lib/jwt/claims/not_before.rb +32 -0
  16. data/lib/jwt/claims/numeric.rb +45 -0
  17. data/lib/jwt/claims/required.rb +33 -0
  18. data/lib/jwt/claims/subject.rb +30 -0
  19. data/lib/jwt/claims/verifier.rb +61 -0
  20. data/lib/jwt/claims.rb +67 -0
  21. data/lib/jwt/configuration/container.rb +20 -1
  22. data/lib/jwt/configuration/decode_configuration.rb +24 -0
  23. data/lib/jwt/configuration/jwk_configuration.rb +1 -0
  24. data/lib/jwt/configuration.rb +8 -0
  25. data/lib/jwt/decode.rb +42 -81
  26. data/lib/jwt/encode.rb +17 -60
  27. data/lib/jwt/encoded_token.rb +236 -0
  28. data/lib/jwt/error.rb +32 -1
  29. data/lib/jwt/json.rb +1 -1
  30. data/lib/jwt/jwa/ecdsa.rb +59 -24
  31. data/lib/jwt/jwa/hmac.rb +22 -19
  32. data/lib/jwt/jwa/none.rb +8 -3
  33. data/lib/jwt/jwa/ps.rb +21 -15
  34. data/lib/jwt/jwa/rsa.rb +21 -10
  35. data/lib/jwt/jwa/signing_algorithm.rb +62 -0
  36. data/lib/jwt/jwa/unsupported.rb +9 -8
  37. data/lib/jwt/jwa.rb +76 -35
  38. data/lib/jwt/jwk/ec.rb +54 -65
  39. data/lib/jwt/jwk/hmac.rb +5 -6
  40. data/lib/jwt/jwk/key_base.rb +16 -1
  41. data/lib/jwt/jwk/key_finder.rb +35 -8
  42. data/lib/jwt/jwk/kid_as_key_digest.rb +1 -0
  43. data/lib/jwt/jwk/rsa.rb +7 -4
  44. data/lib/jwt/jwk/set.rb +2 -0
  45. data/lib/jwt/jwk.rb +1 -1
  46. data/lib/jwt/token.rb +131 -0
  47. data/lib/jwt/version.rb +24 -19
  48. data/lib/jwt.rb +18 -4
  49. data/ruby-jwt.gemspec +2 -0
  50. metadata +49 -15
  51. data/lib/jwt/claims_validator.rb +0 -37
  52. data/lib/jwt/deprecations.rb +0 -48
  53. data/lib/jwt/jwa/eddsa.rb +0 -42
  54. data/lib/jwt/jwa/hmac_rbnacl.rb +0 -50
  55. data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +0 -46
  56. data/lib/jwt/jwa/wrapper.rb +0 -26
  57. data/lib/jwt/jwk/okp_rbnacl.rb +0 -110
  58. data/lib/jwt/verify.rb +0 -117
@@ -2,7 +2,29 @@
2
2
 
3
3
  module JWT
4
4
  module Configuration
5
+ # The DecodeConfiguration class holds the configuration settings for decoding JWT tokens.
5
6
  class DecodeConfiguration
7
+ # @!attribute [rw] verify_expiration
8
+ # @return [Boolean] whether to verify the expiration claim.
9
+ # @!attribute [rw] verify_not_before
10
+ # @return [Boolean] whether to verify the not before claim.
11
+ # @!attribute [rw] verify_iss
12
+ # @return [Boolean] whether to verify the issuer claim.
13
+ # @!attribute [rw] verify_iat
14
+ # @return [Boolean] whether to verify the issued at claim.
15
+ # @!attribute [rw] verify_jti
16
+ # @return [Boolean] whether to verify the JWT ID claim.
17
+ # @!attribute [rw] verify_aud
18
+ # @return [Boolean] whether to verify the audience claim.
19
+ # @!attribute [rw] verify_sub
20
+ # @return [Boolean] whether to verify the subject claim.
21
+ # @!attribute [rw] leeway
22
+ # @return [Integer] the leeway in seconds for time-based claims.
23
+ # @!attribute [rw] algorithms
24
+ # @return [Array<String>] the list of acceptable algorithms.
25
+ # @!attribute [rw] required_claims
26
+ # @return [Array<String>] the list of required claims.
27
+
6
28
  attr_accessor :verify_expiration,
7
29
  :verify_not_before,
8
30
  :verify_iss,
@@ -14,6 +36,7 @@ module JWT
14
36
  :algorithms,
15
37
  :required_claims
16
38
 
39
+ # Initializes a new DecodeConfiguration instance with default settings.
17
40
  def initialize
18
41
  @verify_expiration = true
19
42
  @verify_not_before = true
@@ -27,6 +50,7 @@ module JWT
27
50
  @required_claims = []
28
51
  end
29
52
 
53
+ # @api private
30
54
  def to_h
31
55
  {
32
56
  verify_expiration: verify_expiration,
@@ -5,6 +5,7 @@ require_relative '../jwk/thumbprint'
5
5
 
6
6
  module JWT
7
7
  module Configuration
8
+ # @api private
8
9
  class JwkConfiguration
9
10
  def initialize
10
11
  self.kid_generator_type = :key_digest
@@ -3,11 +3,19 @@
3
3
  require_relative 'configuration/container'
4
4
 
5
5
  module JWT
6
+ # The Configuration module provides methods to configure JWT settings.
6
7
  module Configuration
8
+ # Configures the JWT settings.
9
+ #
10
+ # @yield [config] Gives the current configuration to the block.
11
+ # @yieldparam config [JWT::Configuration::Container] the configuration container.
7
12
  def configure
8
13
  yield(configuration)
9
14
  end
10
15
 
16
+ # Returns the JWT configuration container.
17
+ #
18
+ # @return [JWT::Configuration::Container] the configuration container.
11
19
  def configuration
12
20
  @configuration ||= ::JWT::Configuration::Container.new
13
21
  end
data/lib/jwt/decode.rb CHANGED
@@ -1,90 +1,90 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
-
5
- require 'jwt/verify'
6
4
  require 'jwt/x5c_key_finder'
7
5
 
8
- # JWT::Decode module
9
6
  module JWT
10
- # Decoding logic for JWT
7
+ # The Decode class is responsible for decoding and verifying JWT tokens.
11
8
  class Decode
9
+ # Order is very important - first check for string keys, next for symbols
10
+ ALGORITHM_KEYS = ['algorithm',
11
+ :algorithm,
12
+ 'algorithms',
13
+ :algorithms].freeze
14
+ # Initializes a new Decode instance.
15
+ #
16
+ # @param jwt [String] the JWT to decode.
17
+ # @param key [String, Array<String>] the key(s) to use for verification.
18
+ # @param verify [Boolean] whether to verify the token's signature.
19
+ # @param options [Hash] additional options for decoding and verification.
20
+ # @param keyfinder [Proc] an optional key finder block to dynamically find the key for verification.
21
+ # @raise [JWT::DecodeError] if decoding or verification fails.
12
22
  def initialize(jwt, key, verify, options, &keyfinder)
13
23
  raise JWT::DecodeError, 'Nil JSON web token' unless jwt
14
24
 
15
- @jwt = jwt
25
+ @token = EncodedToken.new(jwt)
16
26
  @key = key
17
27
  @options = options
18
- @segments = jwt.split('.')
19
28
  @verify = verify
20
- @signature = ''
21
29
  @keyfinder = keyfinder
22
30
  end
23
31
 
32
+ # Decodes the JWT token and verifies its segments if verification is enabled.
33
+ #
34
+ # @return [Array<Hash>] an array containing the decoded payload and header.
24
35
  def decode_segments
25
36
  validate_segment_count!
26
37
  if @verify
27
- decode_signature
28
38
  verify_algo
29
39
  set_key
30
40
  verify_signature
31
- verify_claims
41
+ Claims::DecodeVerifier.verify!(token.unverified_payload, @options)
32
42
  end
33
- raise JWT::DecodeError, 'Not enough or too many segments' unless header && payload
34
43
 
35
- [payload, header]
44
+ [token.unverified_payload, token.header]
36
45
  end
37
46
 
38
47
  private
39
48
 
40
- def verify_signature
41
- return unless @key || @verify
49
+ attr_reader :token
42
50
 
51
+ def verify_signature
43
52
  return if none_algorithm?
44
53
 
45
54
  raise JWT::DecodeError, 'No verification key available' unless @key
46
55
 
47
- return if Array(@key).any? { |key| verify_signature_for?(key) }
48
-
49
- raise JWT::VerificationError, 'Signature verification failed'
56
+ token.verify_signature!(algorithm: allowed_and_valid_algorithms, key: @key)
50
57
  end
51
58
 
52
59
  def verify_algo
53
60
  raise JWT::IncorrectAlgorithm, 'An algorithm must be specified' if allowed_algorithms.empty?
61
+ raise JWT::DecodeError, 'Token header not a JSON object' unless token.header.is_a?(Hash)
54
62
  raise JWT::IncorrectAlgorithm, 'Token is missing alg header' unless alg_in_header
55
63
  raise JWT::IncorrectAlgorithm, 'Expected a different algorithm' if allowed_and_valid_algorithms.empty?
56
64
  end
57
65
 
58
66
  def set_key
59
67
  @key = find_key(&@keyfinder) if @keyfinder
60
- @key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks], allow_nil_kid: @options[:allow_nil_kid]).key_for(header['kid']) if @options[:jwks]
61
- return unless (x5c_options = @options[:x5c])
68
+ if @options[:jwks]
69
+ @key = ::JWT::JWK::KeyFinder.new(
70
+ jwks: @options[:jwks],
71
+ allow_nil_kid: @options[:allow_nil_kid],
72
+ key_fields: @options[:key_fields]
73
+ ).call(token)
74
+ end
62
75
 
63
- @key = X5cKeyFinder.new(x5c_options[:root_certificates], x5c_options[:crls]).from(header['x5c'])
64
- end
76
+ return unless (x5c_options = @options[:x5c])
65
77
 
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
78
+ @key = X5cKeyFinder.new(x5c_options[:root_certificates], x5c_options[:crls]).from(token.header['x5c'])
70
79
  end
71
80
 
72
81
  def allowed_and_valid_algorithms
73
82
  @allowed_and_valid_algorithms ||= allowed_algorithms.select { |alg| alg.valid_alg?(alg_in_header) }
74
83
  end
75
84
 
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
85
  def given_algorithms
83
- ALGORITHM_KEYS.each do |alg_key|
84
- alg = @options[alg_key]
85
- return Array(alg) if alg
86
- end
87
- []
86
+ alg_key = ALGORITHM_KEYS.find { |key| @options[key] }
87
+ Array(@options[alg_key])
88
88
  end
89
89
 
90
90
  def allowed_algorithms
@@ -92,71 +92,32 @@ module JWT
92
92
  end
93
93
 
94
94
  def resolve_allowed_algorithms
95
- algs = given_algorithms.map { |alg| JWA.create(alg) }
96
-
97
- sort_by_alg_header(algs)
98
- end
99
-
100
- # Move algorithms matching the JWT alg header to the beginning of the list
101
- def sort_by_alg_header(algs)
102
- return algs if algs.size <= 1
103
-
104
- algs.partition { |alg| alg.valid_alg?(alg_in_header) }.flatten
95
+ given_algorithms.map { |alg| JWA.resolve(alg) }
105
96
  end
106
97
 
107
98
  def find_key(&keyfinder)
108
- key = (keyfinder.arity == 2 ? yield(header, payload) : yield(header))
99
+ key = (keyfinder.arity == 2 ? yield(token.header, token.unverified_payload) : yield(token.header))
109
100
  # key can be of type [string, nil, OpenSSL::PKey, Array]
110
101
  return key if key && !Array(key).empty?
111
102
 
112
103
  raise JWT::DecodeError, 'No verification key available'
113
104
  end
114
105
 
115
- def verify_claims
116
- Verify.verify_claims(payload, @options)
117
- Verify.verify_required_claims(payload, @options)
118
- end
119
-
120
106
  def validate_segment_count!
121
- return if segment_length == 3
122
- return if !@verify && segment_length == 2 # If no verifying required, the signature is not needed
123
- return if segment_length == 2 && none_algorithm?
107
+ segment_count = token.jwt.count('.') + 1
108
+ return if segment_count == 3
109
+ return if !@verify && segment_count == 2 # If no verifying required, the signature is not needed
110
+ return if segment_count == 2 && none_algorithm?
124
111
 
125
112
  raise JWT::DecodeError, 'Not enough or too many segments'
126
113
  end
127
114
 
128
- def segment_length
129
- @segments.count
130
- end
131
-
132
115
  def none_algorithm?
133
116
  alg_in_header == 'none'
134
117
  end
135
118
 
136
- def decode_signature
137
- @signature = ::JWT::Base64.url_decode(@segments[2] || '')
138
- end
139
-
140
119
  def alg_in_header
141
- header['alg']
142
- end
143
-
144
- def header
145
- @header ||= parse_and_decode @segments[0]
146
- end
147
-
148
- def payload
149
- @payload ||= parse_and_decode @segments[1]
150
- end
151
-
152
- def signing_input
153
- @segments.first(2).join('.')
154
- end
155
-
156
- def parse_and_decode(segment)
157
- JWT::JSON.parse(::JWT::Base64.url_decode(segment))
158
- rescue ::JSON::ParserError
159
- raise JWT::DecodeError, 'Invalid segment encoding'
120
+ token.header['alg']
160
121
  end
161
122
  end
162
123
  end
data/lib/jwt/encode.rb CHANGED
@@ -1,73 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'jwa'
4
- require_relative 'claims_validator'
5
4
 
6
- # JWT::Encode module
7
5
  module JWT
8
- # Encoding logic for JWT
6
+ # The Encode class is responsible for encoding JWT tokens.
9
7
  class Encode
10
- ALG_KEY = 'alg'
11
-
8
+ # Initializes a new Encode instance.
9
+ #
10
+ # @param options [Hash] the options for encoding the JWT token.
11
+ # @option options [Hash] :payload the payload of the JWT token.
12
+ # @option options [Hash] :headers the headers of the JWT token.
13
+ # @option options [String] :key the key used to sign the JWT token.
14
+ # @option options [String] :algorithm the algorithm used to sign the JWT token.
12
15
  def initialize(options)
13
- @payload = options[:payload]
14
- @key = options[:key]
15
- @algorithm = JWA.create(options[:algorithm])
16
- @headers = options[:headers].transform_keys(&:to_s)
17
- @headers[ALG_KEY] = @algorithm.alg
16
+ @token = Token.new(payload: options[:payload], header: options[:headers])
17
+ @key = options[:key]
18
+ @algorithm = options[:algorithm]
18
19
  end
19
20
 
21
+ # Encodes the JWT token and returns its segments.
22
+ #
23
+ # @return [String] the encoded JWT token.
20
24
  def segments
21
- validate_claims!
22
- combine(encoded_header_and_payload, encoded_signature)
23
- end
24
-
25
- private
26
-
27
- def encoded_header
28
- @encoded_header ||= encode_header
29
- end
30
-
31
- def encoded_payload
32
- @encoded_payload ||= encode_payload
33
- end
34
-
35
- def encoded_signature
36
- @encoded_signature ||= encode_signature
37
- end
38
-
39
- def encoded_header_and_payload
40
- @encoded_header_and_payload ||= combine(encoded_header, encoded_payload)
41
- end
42
-
43
- def encode_header
44
- encode_data(@headers)
45
- end
46
-
47
- def encode_payload
48
- encode_data(@payload)
49
- end
50
-
51
- def signature
52
- @algorithm.sign(data: encoded_header_and_payload, signing_key: @key)
53
- end
54
-
55
- def validate_claims!
56
- return unless @payload.is_a?(Hash)
57
-
58
- ClaimsValidator.new(@payload).validate!
59
- end
60
-
61
- def encode_signature
62
- ::JWT::Base64.url_encode(signature)
63
- end
64
-
65
- def encode_data(data)
66
- ::JWT::Base64.url_encode(JWT::JSON.generate(data))
67
- end
68
-
69
- def combine(*parts)
70
- parts.join('.')
25
+ @token.verify_claims!(:numeric)
26
+ @token.sign!(algorithm: @algorithm, key: @key)
27
+ @token.jwt
71
28
  end
72
29
  end
73
30
  end
@@ -0,0 +1,236 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ # Represents an encoded JWT token
5
+ #
6
+ # Processing an encoded and signed token:
7
+ #
8
+ # token = JWT::Token.new(payload: {pay: 'load'})
9
+ # token.sign!(algorithm: 'HS256', key: 'secret')
10
+ #
11
+ # encoded_token = JWT::EncodedToken.new(token.jwt)
12
+ # encoded_token.verify_signature!(algorithm: 'HS256', key: 'secret')
13
+ # encoded_token.payload # => {'pay' => 'load'}
14
+ class EncodedToken
15
+ # @private
16
+ # Allow access to the unverified payload for claim verification.
17
+ class ClaimsContext
18
+ extend Forwardable
19
+
20
+ def_delegators :@token, :header, :unverified_payload
21
+
22
+ def initialize(token)
23
+ @token = token
24
+ end
25
+
26
+ def payload
27
+ unverified_payload
28
+ end
29
+ end
30
+
31
+ DEFAULT_CLAIMS = [:exp].freeze
32
+
33
+ private_constant(:DEFAULT_CLAIMS)
34
+
35
+ # Returns the original token provided to the class.
36
+ # @return [String] The JWT token.
37
+ attr_reader :jwt
38
+
39
+ # Initializes a new EncodedToken instance.
40
+ #
41
+ # @param jwt [String] the encoded JWT token.
42
+ # @raise [ArgumentError] if the provided JWT is not a String.
43
+ def initialize(jwt)
44
+ raise ArgumentError, 'Provided JWT must be a String' unless jwt.is_a?(String)
45
+
46
+ @jwt = jwt
47
+ @signature_verified = false
48
+ @claims_verified = false
49
+
50
+ @encoded_header, @encoded_payload, @encoded_signature = jwt.split('.')
51
+ end
52
+
53
+ # Returns the decoded signature of the JWT token.
54
+ #
55
+ # @return [String] the decoded signature.
56
+ def signature
57
+ @signature ||= ::JWT::Base64.url_decode(encoded_signature || '')
58
+ end
59
+
60
+ # Returns the encoded signature of the JWT token.
61
+ #
62
+ # @return [String] the encoded signature.
63
+ attr_reader :encoded_signature
64
+
65
+ # Returns the decoded header of the JWT token.
66
+ #
67
+ # @return [Hash] the header.
68
+ def header
69
+ @header ||= parse_and_decode(@encoded_header)
70
+ end
71
+
72
+ # Returns the encoded header of the JWT token.
73
+ #
74
+ # @return [String] the encoded header.
75
+ attr_reader :encoded_header
76
+
77
+ # Returns the payload of the JWT token. Access requires the signature and claims to have been verified.
78
+ #
79
+ # @return [Hash] the payload.
80
+ # @raise [JWT::DecodeError] if the signature has not been verified.
81
+ def payload
82
+ raise JWT::DecodeError, 'Verify the token signature before accessing the payload' unless @signature_verified
83
+ raise JWT::DecodeError, 'Verify the token claims before accessing the payload' unless @claims_verified
84
+
85
+ decoded_payload
86
+ end
87
+
88
+ # Returns the payload of the JWT token without requiring the signature to have been verified.
89
+ # @return [Hash] the payload.
90
+ def unverified_payload
91
+ decoded_payload
92
+ end
93
+
94
+ # Sets or returns the encoded payload of the JWT token.
95
+ #
96
+ # @return [String] the encoded payload.
97
+ attr_accessor :encoded_payload
98
+
99
+ # Returns the signing input of the JWT token.
100
+ #
101
+ # @return [String] the signing input.
102
+ def signing_input
103
+ [encoded_header, encoded_payload].join('.')
104
+ end
105
+
106
+ # Verifies the token signature and claims.
107
+ # By default it verifies the 'exp' claim.
108
+ #
109
+ # @example
110
+ # encoded_token.verify!(signature: { algorithm: 'HS256', key: 'secret' }, claims: [:exp])
111
+ #
112
+ # @param signature [Hash] the parameters for signature verification (see {#verify_signature!}).
113
+ # @param claims [Array<Symbol>, Hash] the claims to verify (see {#verify_claims!}).
114
+ # @return [nil]
115
+ # @raise [JWT::DecodeError] if the signature or claim verification fails.
116
+ def verify!(signature:, claims: nil)
117
+ verify_signature!(**signature)
118
+ claims.is_a?(Array) ? verify_claims!(*claims) : verify_claims!(claims)
119
+ nil
120
+ end
121
+
122
+ # Verifies the token signature and claims.
123
+ # By default it verifies the 'exp' claim.
124
+
125
+ # @param signature [Hash] the parameters for signature verification (see {#verify_signature!}).
126
+ # @param claims [Array<Symbol>, Hash] the claims to verify (see {#verify_claims!}).
127
+ # @return [Boolean] true if the signature and claims are valid, false otherwise.
128
+ def valid?(signature:, claims: nil)
129
+ valid_signature?(**signature) &&
130
+ (claims.is_a?(Array) ? valid_claims?(*claims) : valid_claims?(claims))
131
+ end
132
+
133
+ # Verifies the signature of the JWT token.
134
+ #
135
+ # @param algorithm [String, Array<String>, Object, Array<Object>] the algorithm(s) to use for verification.
136
+ # @param key [String, Array<String>] the key(s) to use for verification.
137
+ # @param key_finder [#call] an object responding to `call` to find the key for verification.
138
+ # @return [nil]
139
+ # @raise [JWT::VerificationError] if the signature verification fails.
140
+ # @raise [ArgumentError] if neither key nor key_finder is provided, or if both are provided.
141
+ def verify_signature!(algorithm:, key: nil, key_finder: nil)
142
+ return if valid_signature?(algorithm: algorithm, key: key, key_finder: key_finder)
143
+
144
+ raise JWT::VerificationError, 'Signature verification failed'
145
+ end
146
+
147
+ # Checks if the signature of the JWT token is valid.
148
+ #
149
+ # @param algorithm [String, Array<String>, Object, Array<Object>] the algorithm(s) to use for verification.
150
+ # @param key [String, Array<String>, JWT::JWK::KeyBase, Array<JWT::JWK::KeyBase>] the key(s) to use for verification.
151
+ # @param key_finder [#call] an object responding to `call` to find the key for verification.
152
+ # @return [Boolean] true if the signature is valid, false otherwise.
153
+ def valid_signature?(algorithm: nil, key: nil, key_finder: nil)
154
+ raise ArgumentError, 'Provide either key or key_finder, not both or neither' if key.nil? == key_finder.nil?
155
+
156
+ keys = Array(key || key_finder.call(self))
157
+ verifiers = JWA.create_verifiers(algorithms: algorithm, keys: keys, preferred_algorithm: header['alg'])
158
+
159
+ raise JWT::VerificationError, 'No algorithm provided' if verifiers.empty?
160
+
161
+ valid = verifiers.any? do |jwa|
162
+ jwa.verify(data: signing_input, signature: signature)
163
+ end
164
+ valid.tap { |verified| @signature_verified = verified }
165
+ end
166
+
167
+ # Verifies the claims of the token.
168
+ # @param options [Array<Symbol>, Hash] the claims to verify. By default, it checks the 'exp' claim.
169
+ # @raise [JWT::DecodeError] if the claims are invalid.
170
+ def verify_claims!(*options)
171
+ Claims::Verifier.verify!(ClaimsContext.new(self), *claims_options(options)).tap do
172
+ @claims_verified = true
173
+ end
174
+ rescue StandardError
175
+ @claims_verified = false
176
+ raise
177
+ end
178
+
179
+ # Returns the errors of the claims of the token.
180
+ # @param options [Array<Symbol>, Hash] the claims to verify. By default, it checks the 'exp' claim.
181
+ # @return [Array<Symbol>] the errors of the claims.
182
+ def claim_errors(*options)
183
+ Claims::Verifier.errors(ClaimsContext.new(self), *claims_options(options))
184
+ end
185
+
186
+ # Returns whether the claims of the token are valid.
187
+ # @param options [Array<Symbol>, Hash] the claims to verify. By default, it checks the 'exp' claim.
188
+ # @return [Boolean] whether the claims are valid.
189
+ def valid_claims?(*options)
190
+ claim_errors(*claims_options(options)).empty?.tap { |verified| @claims_verified = verified }
191
+ end
192
+
193
+ alias to_s jwt
194
+
195
+ private
196
+
197
+ def claims_options(options)
198
+ return DEFAULT_CLAIMS if options.first.nil?
199
+
200
+ options
201
+ end
202
+
203
+ def decode_payload
204
+ raise JWT::DecodeError, 'Encoded payload is empty' if encoded_payload == ''
205
+
206
+ if unencoded_payload?
207
+ verify_claims!(crit: ['b64'])
208
+ return parse_unencoded(encoded_payload)
209
+ end
210
+
211
+ parse_and_decode(encoded_payload)
212
+ end
213
+
214
+ def unencoded_payload?
215
+ header['b64'] == false
216
+ end
217
+
218
+ def parse_and_decode(segment)
219
+ parse(::JWT::Base64.url_decode(segment || ''))
220
+ end
221
+
222
+ def parse_unencoded(segment)
223
+ parse(segment)
224
+ end
225
+
226
+ def parse(segment)
227
+ JWT::JSON.parse(segment)
228
+ rescue ::JSON::ParserError
229
+ raise JWT::DecodeError, 'Invalid segment encoding'
230
+ end
231
+
232
+ def decoded_payload
233
+ @decoded_payload ||= decode_payload
234
+ end
235
+ end
236
+ end
data/lib/jwt/error.rb CHANGED
@@ -1,23 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JWT
4
+ # The EncodeError class is raised when there is an error encoding a JWT.
4
5
  class EncodeError < StandardError; end
6
+
7
+ # The DecodeError class is raised when there is an error decoding a JWT.
5
8
  class DecodeError < StandardError; end
6
- class RequiredDependencyError < StandardError; end
7
9
 
10
+ # The VerificationError class is raised when there is an error verifying a JWT.
8
11
  class VerificationError < DecodeError; end
12
+
13
+ # The ExpiredSignature class is raised when the JWT signature has expired.
9
14
  class ExpiredSignature < DecodeError; end
15
+
16
+ # The IncorrectAlgorithm class is raised when the JWT algorithm is incorrect.
10
17
  class IncorrectAlgorithm < DecodeError; end
18
+
19
+ # The ImmatureSignature class is raised when the JWT signature is immature.
11
20
  class ImmatureSignature < DecodeError; end
21
+
22
+ # The InvalidIssuerError class is raised when the JWT issuer is invalid.
12
23
  class InvalidIssuerError < DecodeError; end
24
+
25
+ # The UnsupportedEcdsaCurve class is raised when the ECDSA curve is unsupported.
13
26
  class UnsupportedEcdsaCurve < IncorrectAlgorithm; end
27
+
28
+ # The InvalidIatError class is raised when the JWT issued at (iat) claim is invalid.
14
29
  class InvalidIatError < DecodeError; end
30
+
31
+ # The InvalidAudError class is raised when the JWT audience (aud) claim is invalid.
15
32
  class InvalidAudError < DecodeError; end
33
+
34
+ # The InvalidSubError class is raised when the JWT subject (sub) claim is invalid.
16
35
  class InvalidSubError < DecodeError; end
36
+
37
+ # The InvalidCritError class is raised when the JWT crit header is invalid.
38
+ class InvalidCritError < DecodeError; end
39
+
40
+ # The InvalidJtiError class is raised when the JWT ID (jti) claim is invalid.
17
41
  class InvalidJtiError < DecodeError; end
42
+
43
+ # The InvalidPayload class is raised when the JWT payload is invalid.
18
44
  class InvalidPayload < DecodeError; end
45
+
46
+ # The MissingRequiredClaim class is raised when a required claim is missing from the JWT.
19
47
  class MissingRequiredClaim < DecodeError; end
48
+
49
+ # The Base64DecodeError class is raised when there is an error decoding a Base64-encoded string.
20
50
  class Base64DecodeError < DecodeError; end
21
51
 
52
+ # The JWKError class is raised when there is an error with the JSON Web Key (JWK).
22
53
  class JWKError < DecodeError; end
23
54
  end
data/lib/jwt/json.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require 'json'
4
4
 
5
5
  module JWT
6
- # JSON wrapper
6
+ # @api private
7
7
  class JSON
8
8
  class << self
9
9
  def generate(data)