class Rack::JWT::Auth
Authentication middleware
Constants
- BEARER_TOKEN_REGEX
The last segment gets dropped for ‘none’ algorithm since there is no signature so both of these patterns are valid. All character chunks are base64url format and periods.
Bearer abc123.abc123.abc123 Bearer abc123.abc123.
- DEFAULT_ALGORITHM
- SUPPORTED_ALGORITHMS
Attributes
exclude[R]
options[R]
secret[R]
verify[R]
Public Class Methods
new(app, opts = {})
click to toggle source
Initialization should fail fast with an ArgumentError if any args are invalid.
# File lib/rack/jwt/auth.rb, line 43 def initialize(app, opts = {}) @app = app @secret = opts.fetch(:secret, nil) @verify = opts.fetch(:verify, true) @options = opts.fetch(:options, {}) @exclude = opts.fetch(:exclude, []) @secret = @secret.strip if @secret.is_a?(String) @options[:algorithm] = DEFAULT_ALGORITHM if @options[:algorithm].nil? check_secret_type! check_secret! check_secret_and_verify_for_none_alg! check_verify_type! check_options_type! check_valid_algorithm! check_exclude_type! end
Public Instance Methods
call(env)
click to toggle source
# File lib/rack/jwt/auth.rb, line 62 def call(env) if path_matches_excluded_path?(env) @app.call(env) elsif missing_auth_header?(env) return_error('Missing Authorization header') elsif invalid_auth_header?(env) return_error('Invalid Authorization header format') else verify_token(env) end end
Private Instance Methods
check_exclude_type!()
click to toggle source
# File lib/rack/jwt/auth.rb, line 149 def check_exclude_type! unless @exclude.is_a?(Array) raise ArgumentError, 'exclude argument must be an Array' end @exclude.each do |x| unless x.is_a?(String) raise ArgumentError, 'each exclude Array element must be a String' end if x.empty? raise ArgumentError, 'each exclude Array element must not be empty' end unless x.start_with?('/') raise ArgumentError, 'each exclude Array element must start with a /' end end end
check_options_type!()
click to toggle source
# File lib/rack/jwt/auth.rb, line 137 def check_options_type! raise ArgumentError, 'options argument must be a Hash' unless options.is_a?(Hash) end
check_secret!()
click to toggle source
# File lib/rack/jwt/auth.rb, line 115 def check_secret! if @secret.nil? || (@secret.is_a?(String) && @secret.empty?) unless @options[:algorithm] == 'none' raise ArgumentError, 'secret argument can only be nil/empty for the "none" algorithm' end end end
check_secret_and_verify_for_none_alg!()
click to toggle source
# File lib/rack/jwt/auth.rb, line 123 def check_secret_and_verify_for_none_alg! if @options && @options[:algorithm] && @options[:algorithm] == 'none' unless @secret.nil? && @verify.is_a?(FalseClass) raise ArgumentError, 'when "none" the secret must be "nil" and verify "false"' end end end
check_secret_type!()
click to toggle source
# File lib/rack/jwt/auth.rb, line 109 def check_secret_type! unless Token.secret_of_valid_type?(@secret) raise ArgumentError, 'secret argument must be a valid type' end end
check_valid_algorithm!()
click to toggle source
# File lib/rack/jwt/auth.rb, line 141 def check_valid_algorithm! unless @options && @options[:algorithm] && SUPPORTED_ALGORITHMS.include?(@options[:algorithm]) raise ArgumentError, 'algorithm argument must be a supported type' end end
check_verify_type!()
click to toggle source
# File lib/rack/jwt/auth.rb, line 131 def check_verify_type! unless verify.is_a?(TrueClass) || verify.is_a?(FalseClass) raise ArgumentError, 'verify argument must be true or false' end end
invalid_auth_header?(env)
click to toggle source
# File lib/rack/jwt/auth.rb, line 177 def invalid_auth_header?(env) !valid_auth_header?(env) end
missing_auth_header?(env)
click to toggle source
# File lib/rack/jwt/auth.rb, line 181 def missing_auth_header?(env) env['HTTP_AUTHORIZATION'].nil? || env['HTTP_AUTHORIZATION'].strip.empty? end
path_matches_excluded_path?(env)
click to toggle source
# File lib/rack/jwt/auth.rb, line 169 def path_matches_excluded_path?(env) @exclude.any? { |ex| env['PATH_INFO'].start_with?(ex) } end
return_error(message)
click to toggle source
# File lib/rack/jwt/auth.rb, line 185 def return_error(message) body = { error: message }.to_json headers = { 'Content-Type' => 'application/json' } [401, headers, [body]] end
valid_auth_header?(env)
click to toggle source
# File lib/rack/jwt/auth.rb, line 173 def valid_auth_header?(env) env['HTTP_AUTHORIZATION'] =~ BEARER_TOKEN_REGEX end
verify_token(env)
click to toggle source
# File lib/rack/jwt/auth.rb, line 76 def verify_token(env) # extract the token from the Authorization: Bearer header # with a regex capture group. token = BEARER_TOKEN_REGEX.match(env['HTTP_AUTHORIZATION'])[1] begin decoded_token = Token.decode(token, @secret, @verify, @options) env['jwt.payload'] = decoded_token.first env['jwt.header'] = decoded_token.last @app.call(env) rescue ::JWT::VerificationError return_error('Invalid JWT token : Signature Verification Error') rescue ::JWT::ExpiredSignature return_error('Invalid JWT token : Expired Signature (exp)') rescue ::JWT::IncorrectAlgorithm return_error('Invalid JWT token : Incorrect Key Algorithm') rescue ::JWT::ImmatureSignature return_error('Invalid JWT token : Immature Signature (nbf)') rescue ::JWT::InvalidIssuerError return_error('Invalid JWT token : Invalid Issuer (iss)') rescue ::JWT::InvalidIatError return_error('Invalid JWT token : Invalid Issued At (iat)') rescue ::JWT::InvalidAudError return_error('Invalid JWT token : Invalid Audience (aud)') rescue ::JWT::InvalidSubError return_error('Invalid JWT token : Invalid Subject (sub)') rescue ::JWT::InvalidJtiError return_error('Invalid JWT token : Invalid JWT ID (jti)') rescue ::JWT::DecodeError return_error('Invalid JWT token : Decode Error') end end