Skip to content

Commit

Permalink
Merge pull request #266 from ab320012/master
Browse files Browse the repository at this point in the history
Improving code quality of JWT module
  • Loading branch information
excpt authored May 6, 2018
2 parents fd7f742 + 025ceb8 commit eef38b2
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 38 deletions.
63 changes: 41 additions & 22 deletions lib/jwt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,61 @@ def encode(payload, key, algorithm = 'HS256', header_fields = {})
encoder.segments
end

def decode(jwt, key = nil, verify = true, custom_options = {}, &keyfinder)
def decode(jwt, key = nil, verify = true, options = {}, &keyfinder)
raise(JWT::DecodeError, 'Nil JSON web token') unless jwt

merged_options = DEFAULT_OPTIONS.merge(custom_options)

decoder = Decode.new jwt, verify
header, payload, signature, signing_input = decoder.decode_segments
decode_verify_signature(key, header, payload, signature, signing_input, merged_options, &keyfinder) if verify

Verify.verify_claims(payload, merged_options) if verify
@jwt = jwt
@key = key
@verify = verify
@options = DEFAULT_OPTIONS.merge(options)
@header,
@payload,
@signature,
@signing_input = Decode.new(jwt, verify).decode_segments
if verify?
verify_signature(&keyfinder)
verify_claims
end

raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
raise(JWT::DecodeError, 'Not enough or too many segments') unless @header && @payload

[payload, header]
[@payload, @header]
end

def decode_verify_signature(key, header, payload, signature, signing_input, options, &keyfinder)
algo, key = signature_algorithm_and_key(header, payload, key, &keyfinder)
private_class_method

raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') if allowed_algorithms(options).empty?
raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless allowed_algorithms(options).include?(algo)
def verify_signature(&keyfinder)
@key = find_key(&keyfinder) if keyfinder

Signature.verify(algo, key, signing_input, signature)
raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') if allowed_algorithms.empty?
raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless options_includes_algo_in_header?

Signature.verify(@header['alg'], @key, @signing_input, @signature)
end

def signature_algorithm_and_key(header, payload, key, &keyfinder)
key = (keyfinder.arity == 2 ? yield(header, payload) : yield(header)) if keyfinder
def find_key(&keyfinder)
key = (keyfinder.arity == 2 ? yield(@header, @payload) : yield(@header))
raise JWT::DecodeError, 'No verification key available' unless key
[header['alg'], key]
key
end

def allowed_algorithms(options)
if options.key?(:algorithm)
[options[:algorithm]]
def allowed_algorithms
if @options.key?(:algorithm)
[@options[:algorithm]]
else
options[:algorithms] || []
@options[:algorithms] || []
end
end

def verify?
@verify
end

def verify_claims
Verify.verify_claims(@payload, @options)
end

def options_includes_algo_in_header?
allowed_algorithms.include? @header['alg']
end
end
50 changes: 34 additions & 16 deletions lib/jwt/decode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,60 @@
module JWT
# Decoding logic for JWT
class Decode
attr_reader :header, :payload, :signature

def self.base64url_decode(str)
str += '=' * (4 - str.length.modulo(4))
Base64.decode64(str.tr('-_', '+/'))
end

def initialize(jwt, verify)
@jwt = jwt
@segments = jwt.split('.')
@verify = verify
@header = ''
@payload = ''
@signature = ''
end

def decode_segments
header_segment, payload_segment, crypto_segment = raw_segments
@header, @payload = decode_header_and_payload(header_segment, payload_segment)
@signature = Decode.base64url_decode(crypto_segment.to_s) if @verify
signing_input = [header_segment, payload_segment].join('.')
[@header, @payload, @signature, signing_input]
validate_segment_count
decode_crypto if @verify
return_values
end

private

def raw_segments
segments = @jwt.split('.')
required_num_segments = @verify ? [3] : [2, 3]
raise(JWT::DecodeError, 'Not enough or too many segments') unless required_num_segments.include? segments.length
segments
def validate_segment_count
raise(JWT::DecodeError, 'Not enough or too many segments') unless
(@verify && segment_length != 3) ||
(segment_length != 3 || segment_length != 2)
end

def segment_length
@segments.count
end

def decode_crypto
@signature = Decode.base64url_decode(@segments[2])
end

def return_values
[header, payload, @signature, signing_input]
end

def header
parse_and_decode @segments[0]
end

def payload
parse_and_decode @segments[1]
end

def signing_input
@segments.first(2).join('.')
end

def decode_header_and_payload(header_segment, payload_segment)
header = JSON.parse(Decode.base64url_decode(header_segment))
payload = JSON.parse(Decode.base64url_decode(payload_segment))
[header, payload]
def parse_and_decode(segment)
JSON.parse(Decode.base64url_decode(segment))
rescue JSON::ParserError
raise JWT::DecodeError, 'Invalid segment encoding'
end
Expand Down

0 comments on commit eef38b2

Please sign in to comment.