Class: Grape::Jwt::Authentication::JwtHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/grape/jwt/authentication/jwt_handler.rb

Overview

Take care of the token validation and verification on this Rack middleware. It is a self contained implementation of a valid Rack handler which checks for a common JWT token Authorization header and calls a user given verification block which performs the database lookup or whatever is necessary for the verification.

Defined Under Namespace

Classes: AuthenticationError, MalformedHeaderError

Constant Summary collapse

JWT_PART_REGEX =

A generic JWT part, the full token contains three parts separated by a period.

/([a-zA-Z0-9\-_]+)?/.freeze
JWT_REGEX =

A common JWT validation regex which meets the RFC specs.

Regexp.new("^#{([JWT_PART_REGEX] * 3).join('\.')}$").freeze

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ JwtHandler

Initialize a new Rack middleware for Bearer token processing.

Parameters:

  • app (Proc)

    The regular Rack application

  • options (Hash) (defaults to: {})

    A global-overwritting configuration hash



30
31
32
33
# File 'lib/grape/jwt/authentication/jwt_handler.rb', line 30

def initialize(app, options = {})
  @app = app
  @options = options
end

Instance Method Details

#authenticatorProc

Get the local or global defined authenticator for the JWT handler.

Returns:

  • (Proc)

    The authenticator block



54
55
56
# File 'lib/grape/jwt/authentication/jwt_handler.rb', line 54

def authenticator
  config(:proc, :authenticator)
end

#call(env) ⇒ Object

Perform the authentication logic on the Rack compatible interface.

because thats the auth handling core :reek:TooManyStatements because reek counts exception

Parameters:

  • env (Hash{String => Mixed})

    the Rack environment



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/grape/jwt/authentication/jwt_handler.rb', line 107

def call(env)
  # Unfortunately Grape's middleware stack orders the error
  # handling higher than the formatter. So when a error is
  # raised, the Rack env was not yet analysed and the content
  # type not negotiated. This would result in allways-JSON
  # responses on authentication errors. We want to be smarter
  # here and respond in the requested format on authentication
  # errors, that why we invoke the formatter middleware here.
  Grape::Middleware::Formatter.new(->(_) {}).call(env)

  # Parse the JWT token from the request headers.
  # Downcase the header keys to account for HTTP/2+
  # semantics in Grape 2.0.0+
  lowercase_env = env.transform_keys(&:downcase)
  token = parse_token(lowercase_env['http_authorization'])

  # Inject the parsed token to the Rack environment.
  inject_token_into_env(env, token)

  # Give the parsed token to the user defined block
  # for futher verification. The user given block MUST return
  # a positive result to allow the request to be further
  # processed, or a negative result to stop processing.
  raise AuthenticationError unless authenticator.call(token)

  # Looks like we are on a good path and the given token was
  # valid on all checks. So we continue the regular
  # application logic now.
  @app.call(env)
rescue MalformedHeaderError
  # Call the user defined malformed authentication handler.
  malformed_handler.call(env['HTTP_AUTHORIZATION'], @app)
rescue AuthenticationError
  # Call the user defined failed authentication handler.
  failed_handler.call(token, @app)
end

#config(key, global_key) ⇒ Mixed

A shared configuration lookup helper which selects the requested entry from the local or global configuration object. The local configuration takes presedence over the global one.

Parameters:

  • key (Symbol)

    The local config key

  • global_key (Symbol)

    The global config key

Returns:

  • (Mixed)

    The configuration value



42
43
44
45
46
47
48
49
# File 'lib/grape/jwt/authentication/jwt_handler.rb', line 42

def config(key, global_key)
  block = @options[key]
  unless block
    global_conf = Grape::Jwt::Authentication.configuration
    return global_conf.send(global_key)
  end
  block
end

#failed_handlerProc

Get the local or global defined failed authentication handler for the JWT handler.

Returns:

  • (Proc)

    The failed authentication handler block



70
71
72
# File 'lib/grape/jwt/authentication/jwt_handler.rb', line 70

def failed_handler
  config(:failed, :failed_auth_handler)
end

#inject_token_into_env(env, token) ⇒ Object

Inject the token to the environment as a parsed version. This allows further usage like extracting the subject from the payload when the verification was valid.

Parameters:

  • env (Hash{String => Mixed})

    the Rack environment

  • token (String)

    the token parsed from the HTTP header



93
94
95
96
97
98
99
# File 'lib/grape/jwt/authentication/jwt_handler.rb', line 93

def inject_token_into_env(env, token)
  env['grape_jwt_auth.parsed_token'] = Keyless::Jwt.new(token)
rescue *Keyless::Jwt::RESCUE_JWT_EXCEPTIONS
  env['grape_jwt_auth.parsed_token'] = nil
ensure
  env['grape_jwt_auth.original_token'] = token
end

#malformed_handlerProc

Get the local or global defined malformed authentication handler for the JWT handler.

Returns:

  • (Proc)

    The malformed authorization handler block



62
63
64
# File 'lib/grape/jwt/authentication/jwt_handler.rb', line 62

def malformed_handler
  config(:malformed, :malformed_auth_handler)
end

#parse_token(header) ⇒ String

Validate the Bearer authentication scheme on the given authorization header and validate the JWT token when it was found.

Parameters:

  • header (String)

    The authorization header value

Returns:

  • (String)

    The parsed and well-formatted JWT

Raises:



80
81
82
83
84
85
# File 'lib/grape/jwt/authentication/jwt_handler.rb', line 80

def parse_token(header)
  token = header.to_s.scan(/^Bearer (.*)/).flatten.first
  raise MalformedHeaderError unless JWT_REGEX =~ token

  token
end