11# frozen_string_literal: true
22
3+ require_relative 'encoded_token/claims_context'
4+ require_relative 'encoded_token/segment_parser'
5+ require_relative 'encoded_token/signature_verifier'
6+
37module JWT
48 # Represents an encoded JWT token
59 #
@@ -11,31 +15,26 @@ module JWT
1115 # encoded_token = JWT::EncodedToken.new(token.jwt)
1216 # encoded_token.verify_signature!(algorithm: 'HS256', key: 'secret')
1317 # encoded_token.payload # => {'pay' => 'load'}
14- class EncodedToken # rubocop:disable Metrics/ClassLength
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-
18+ class EncodedToken
3119 DEFAULT_CLAIMS = [ :exp ] . freeze
32-
3320 private_constant ( :DEFAULT_CLAIMS )
3421
3522 # Returns the original token provided to the class.
3623 # @return [String] The JWT token.
3724 attr_reader :jwt
3825
26+ # Returns the encoded signature of the JWT token.
27+ # @return [String] the encoded signature.
28+ attr_reader :encoded_signature
29+
30+ # Returns the encoded header of the JWT token.
31+ # @return [String] the encoded header.
32+ attr_reader :encoded_header
33+
34+ # Sets or returns the encoded payload of the JWT token.
35+ # @return [String] the encoded payload.
36+ attr_accessor :encoded_payload
37+
3938 # Initializes a new EncodedToken instance.
4039 #
4140 # @param jwt [String] the encoded JWT token.
@@ -46,8 +45,7 @@ def initialize(jwt)
4645 @jwt = jwt
4746 @allow_duplicate_keys = true
4847 @signature_verified = false
49- @claims_verified = false
50-
48+ @claims_verified = false
5149 @encoded_header , @encoded_payload , @encoded_signature = jwt . split ( '.' )
5250 end
5351
@@ -67,57 +65,39 @@ def raise_on_duplicate_keys!
6765 raise JWT ::UnsupportedError , 'Duplicate key detection requires JSON gem >= 2.13.0' unless JSON . supports_duplicate_key_detection?
6866
6967 @allow_duplicate_keys = false
68+ @parser = nil
7069 self
7170 end
7271
7372 # Returns the decoded signature of the JWT token.
74- #
7573 # @return [String] the decoded signature.
7674 def signature
7775 @signature ||= ::JWT ::Base64 . url_decode ( encoded_signature || '' )
7876 end
7977
80- # Returns the encoded signature of the JWT token.
81- #
82- # @return [String] the encoded signature.
83- attr_reader :encoded_signature
84-
8578 # Returns the decoded header of the JWT token.
86- #
8779 # @return [Hash] the header.
8880 def header
89- @header ||= parse_and_decode ( @encoded_header )
81+ @header ||= parser . parse_and_decode ( @encoded_header )
9082 end
9183
92- # Returns the encoded header of the JWT token.
93- #
94- # @return [String] the encoded header.
95- attr_reader :encoded_header
96-
9784 # Returns the payload of the JWT token. Access requires the signature and claims to have been verified.
98- #
9985 # @return [Hash] the payload.
100- # @raise [JWT::DecodeError] if the signature has not been verified.
86+ # @raise [JWT::DecodeError] if the signature or claims have not been verified.
10187 def payload
10288 raise JWT ::DecodeError , 'Verify the token signature before accessing the payload' unless @signature_verified
10389 raise JWT ::DecodeError , 'Verify the token claims before accessing the payload' unless @claims_verified
10490
105- decoded_payload
91+ unverified_payload
10692 end
10793
10894 # Returns the payload of the JWT token without requiring the signature to have been verified.
10995 # @return [Hash] the payload.
11096 def unverified_payload
111- decoded_payload
97+ @unverified_payload ||= decode_payload
11298 end
11399
114- # Sets or returns the encoded payload of the JWT token.
115- #
116- # @return [String] the encoded payload.
117- attr_accessor :encoded_payload
118-
119100 # Returns the signing input of the JWT token.
120- #
121101 # @return [String] the signing input.
122102 def signing_input
123103 [ encoded_header , encoded_payload ] . join ( '.' )
@@ -141,13 +121,12 @@ def verify!(signature:, claims: nil)
141121
142122 # Verifies the token signature and claims.
143123 # By default it verifies the 'exp' claim.
144-
124+ #
145125 # @param signature [Hash] the parameters for signature verification (see {#verify_signature!}).
146126 # @param claims [Array<Symbol>, Hash] the claims to verify (see {#verify_claims!}).
147127 # @return [Boolean] true if the signature and claims are valid, false otherwise.
148128 def valid? ( signature :, claims : nil )
149- valid_signature? ( **signature ) &&
150- ( claims . is_a? ( Array ) ? valid_claims? ( *claims ) : valid_claims? ( claims ) )
129+ valid_signature? ( **signature ) && ( claims . is_a? ( Array ) ? valid_claims? ( *claims ) : valid_claims? ( claims ) )
151130 end
152131
153132 # Verifies the signature of the JWT token.
@@ -171,26 +150,17 @@ def verify_signature!(algorithm:, key: nil, key_finder: nil)
171150 # @param key_finder [#call] an object responding to `call` to find the key for verification.
172151 # @return [Boolean] true if the signature is valid, false otherwise.
173152 def valid_signature? ( algorithm : nil , key : nil , key_finder : nil )
174- raise ArgumentError , 'Provide either key or key_finder, not both or neither' if key . nil? == key_finder . nil?
175-
176- keys = Array ( key || key_finder . call ( self ) )
177- verifiers = JWA . create_verifiers ( algorithms : algorithm , keys : keys , preferred_algorithm : header [ 'alg' ] )
178-
179- raise JWT ::VerificationError , 'No algorithm provided' if verifiers . empty?
180-
181- valid = verifiers . any? do |jwa |
182- jwa . verify ( data : signing_input , signature : signature )
153+ SignatureVerifier . new ( self ) . verify ( algorithm : algorithm , key : key , key_finder : key_finder ) . tap do |valid |
154+ @signature_verified = valid
183155 end
184- valid . tap { |verified | @signature_verified = verified }
185156 end
186157
187158 # Verifies the claims of the token.
188159 # @param options [Array<Symbol>, Hash] the claims to verify. By default, it checks the 'exp' claim.
160+ # @return [nil]
189161 # @raise [JWT::DecodeError] if the claims are invalid.
190162 def verify_claims! ( *options )
191- Claims ::Verifier . verify! ( ClaimsContext . new ( self ) , *claims_options ( options ) ) . tap do
192- @claims_verified = true
193- end
163+ Claims ::Verifier . verify! ( ClaimsContext . new ( self ) , *claims_options ( options ) ) . tap { @claims_verified = true }
194164 rescue StandardError
195165 @claims_verified = false
196166 raise
@@ -215,42 +185,19 @@ def valid_claims?(*options)
215185 private
216186
217187 def claims_options ( options )
218- return DEFAULT_CLAIMS if options . first . nil?
188+ options . first . nil? ? DEFAULT_CLAIMS : options
189+ end
219190
220- options
191+ def parser
192+ @parser ||= SegmentParser . new ( allow_duplicate_keys : @allow_duplicate_keys )
221193 end
222194
223195 def decode_payload
224196 raise JWT ::DecodeError , 'Encoded payload is empty' if encoded_payload == ''
225197
226- if unencoded_payload?
227- verify_claims! ( crit : [ 'b64' ] )
228- return parse_unencoded ( encoded_payload )
229- end
230-
231- parse_and_decode ( encoded_payload )
232- end
233-
234- def unencoded_payload?
235- header [ 'b64' ] == false
236- end
237-
238- def parse_and_decode ( segment )
239- parse ( ::JWT ::Base64 . url_decode ( segment || '' ) )
240- end
241-
242- def parse_unencoded ( segment )
243- parse ( segment )
244- end
245-
246- def parse ( segment )
247- JWT ::JSON . parse ( segment , allow_duplicate_keys : @allow_duplicate_keys )
248- rescue ::JSON ::ParserError
249- raise JWT ::DecodeError , 'Invalid segment encoding'
250- end
198+ return parser . parse_unencoded ( encoded_payload ) . tap { verify_claims! ( crit : [ 'b64' ] ) } if header [ 'b64' ] == false
251199
252- def decoded_payload
253- @decoded_payload ||= decode_payload
200+ parser . parse_and_decode ( encoded_payload )
254201 end
255202 end
256203end
0 commit comments