Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #151 and code refactoring #163

Merged
merged 6 commits into from
Sep 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ pkg
Gemfile.lock
coverage/
.DS_Store
.rbenv-gemsets
.ruby-version
.vscode/
.bundle
bin/
1 change: 0 additions & 1 deletion .rspec
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
--color
--format d
26 changes: 13 additions & 13 deletions lib/jwt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module JWT
'prime256v1' => 'ES256',
'secp384r1' => 'ES384',
'secp521r1' => 'ES512'
}
}.freeze

module_function

Expand All @@ -27,7 +27,7 @@ def sign(algorithm, msg, key)
elsif %w(ES256 ES384 ES512).include?(algorithm)
sign_ecdsa(algorithm, msg, key)
else
fail NotImplementedError, 'Unsupported signing method'
raise NotImplementedError, 'Unsupported signing method'
end
end

Expand All @@ -38,7 +38,7 @@ def sign_rsa(algorithm, msg, private_key)
def sign_ecdsa(algorithm, msg, private_key)
key_algorithm = NAMED_CURVES[private_key.group.curve_name]
if algorithm != key_algorithm
fail IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
end

digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
Expand All @@ -52,7 +52,7 @@ def verify_rsa(algorithm, public_key, signing_input, signature)
def verify_ecdsa(algorithm, public_key, signing_input, signature)
key_algorithm = NAMED_CURVES[public_key.group.curve_name]
if algorithm != key_algorithm
fail IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
end

digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
Expand Down Expand Up @@ -95,7 +95,7 @@ def encode(payload, key, algorithm = 'HS256', header_fields = {})
end

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

options = {
verify_expiration: true,
Expand All @@ -116,7 +116,7 @@ def decoded_segments(jwt, key = nil, verify = true, custom_options = {}, &keyfin


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

options = {
verify_expiration: true,
Expand All @@ -137,32 +137,32 @@ def decode(jwt, key = nil, verify = true, custom_options = {}, &keyfinder)
if verify
algo, key = signature_algorithm_and_key(header, key, &keyfinder)
if merged_options[:algorithm] && algo != merged_options[:algorithm]
fail JWT::IncorrectAlgorithm, 'Expected a different algorithm'
raise JWT::IncorrectAlgorithm, 'Expected a different algorithm'
end
verify_signature(algo, key, signing_input, signature)
end

decoder.verify

fail(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]
end

def signature_algorithm_and_key(header, key, &keyfinder)
key = keyfinder.call(header) if keyfinder
key = yield(header) if keyfinder
[header['alg'], key]
end

def verify_signature(algo, key, signing_input, signature)
if %w(HS256 HS384 HS512).include?(algo)
fail(JWT::VerificationError, 'Signature verification raised') unless secure_compare(signature, sign_hmac(algo, signing_input, key))
raise(JWT::VerificationError, 'Signature verification raised') unless secure_compare(signature, sign_hmac(algo, signing_input, key))
elsif %w(RS256 RS384 RS512).include?(algo)
fail(JWT::VerificationError, 'Signature verification raised') unless verify_rsa(algo, key, signing_input, signature)
raise(JWT::VerificationError, 'Signature verification raised') unless verify_rsa(algo, key, signing_input, signature)
elsif %w(ES256 ES384 ES512).include?(algo)
fail(JWT::VerificationError, 'Signature verification raised') unless verify_ecdsa(algo, key, signing_input, signature)
raise(JWT::VerificationError, 'Signature verification raised') unless verify_ecdsa(algo, key, signing_input, signature)
else
fail JWT::VerificationError, 'Algorithm not supported'
raise JWT::VerificationError, 'Algorithm not supported'
end
rescue OpenSSL::PKey::PKeyError
raise JWT::VerificationError, 'Signature verification raised'
Expand Down
38 changes: 24 additions & 14 deletions lib/jwt/verify.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# frozen_string_literal: true
require 'jwt/error'

module JWT
# JWT verify methods
class Verify
class << self
%w[verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub].each do |method_name|
%w(verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub).each do |method_name|
define_method method_name do |payload, options|
new(payload, options).send(method_name)
end
Expand All @@ -20,12 +21,21 @@ def verify_aud
return unless (options_aud = extract_option(:aud))

if @payload['aud'].is_a?(Array)
fail(
JWT::InvalidAudError,
'Invalid audience'
) unless @payload['aud'].include?(options_aud.to_s)
if options_aud.is_a?(Array)
options_aud.each do |aud|
raise(
JWT::InvalidAudError,
'Invalid audience'
) unless @payload['aud'].include?(aud)
end
else
raise(
JWT::InvalidAudError,
'Invalid audience'
) unless @payload['aud'].include?(options_aud)
end
else
fail(
raise(
JWT::InvalidAudError,
"Invalid audience. Expected #{options_aud}, received #{@payload['aud'] || '<none>'}"
) unless @payload['aud'].to_s == options_aud.to_s
Expand All @@ -36,23 +46,23 @@ def verify_expiration
return unless @payload.include?('exp')

if @payload['exp'].to_i <= (Time.now.to_i - leeway)
fail(JWT::ExpiredSignature, 'Signature has expired')
raise(JWT::ExpiredSignature, 'Signature has expired')
end
end

def verify_iat
return unless @payload.include?('iat')

if !(@payload['iat'].is_a?(Numeric)) || @payload['iat'].to_f > (Time.now.to_f + leeway)
fail(JWT::InvalidIatError, 'Invalid iat')
if !@payload['iat'].is_a?(Numeric) || @payload['iat'].to_f > (Time.now.to_f + leeway)
raise(JWT::InvalidIatError, 'Invalid iat')
end
end

def verify_iss
return unless (options_iss = extract_option(:iss))

if @payload['iss'].to_s != options_iss.to_s
fail(
raise(
JWT::InvalidIssuerError,
"Invalid issuer. Expected #{options_iss}, received #{@payload['iss'] || '<none>'}"
)
Expand All @@ -62,24 +72,24 @@ def verify_iss
def verify_jti
options_verify_jti = extract_option(:verify_jti)
if options_verify_jti.respond_to?(:call)
fail(JWT::InvalidJtiError, 'Invalid jti') unless options_verify_jti.call(@payload['jti'])
raise(JWT::InvalidJtiError, 'Invalid jti') unless options_verify_jti.call(@payload['jti'])
else
fail(JWT::InvalidJtiError, 'Missing jti') if @payload['jti'].to_s.strip.empty?
raise(JWT::InvalidJtiError, 'Missing jti') if @payload['jti'].to_s.strip.empty?
end
end

def verify_not_before
return unless @payload.include?('nbf')

if @payload['nbf'].to_i > (Time.now.to_i + leeway)
fail(JWT::ImmatureSignature, 'Signature nbf has not been reached')
raise(JWT::ImmatureSignature, 'Signature nbf has not been reached')
end
end

def verify_sub
return unless (options_sub = extract_option(:sub))

fail(
raise(
JWT::InvalidSubError,
"Invalid subject. Expected #{options_sub}, received #{@payload['sub'] || '<none>'}"
) unless @payload['sub'].to_s == options_sub.to_s
Expand Down
Loading