Class: Rubino::API::Middleware::Auth

Inherits:
Object
  • Object
show all
Defined in:
lib/rubino/api/middleware/auth.rb

Overview

Bearer-token auth middleware. Sits between JsonParser and the router so unauthorized requests never reach an operation; raises UnauthorizedError which ErrorHandler (one layer up) maps to a 401 JSON response.

Token comparison uses Rack::Utils.secure_compare to avoid timing leaks. SKIP_PATHS allows unauthenticated access to liveness/metrics endpoints so external probes don’t need to carry the API key.

Constant Summary collapse

SKIP_PATHS =
%w[/v1/health /v1/metrics].freeze

Instance Method Summary collapse

Constructor Details

#initialize(app, api_key:) ⇒ Auth

Returns a new instance of Auth.



18
19
20
21
# File 'lib/rubino/api/middleware/auth.rb', line 18

def initialize(app, api_key:)
  @app = app
  @api_key = api_key
end

Instance Method Details

#call(env) ⇒ Object

Raises:



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/rubino/api/middleware/auth.rb', line 23

def call(env)
  return @app.call(env) if SKIP_PATHS.include?(env["PATH_INFO"])

  header = env["HTTP_AUTHORIZATION"].to_s
  # RFC 6750: scheme is case-insensitive, separated from the token by a
  # single space. Match explicitly so a raw token without the "Bearer "
  # prefix is rejected instead of being silently accepted (which is what
  # String#sub would do when the pattern doesn't match).
  match = header.match(/\ABearer (.*)\z/i)
  raise UnauthorizedError, "missing bearer scheme" if match.nil?

  token = match[1]
  raise UnauthorizedError, "missing bearer token" if token.empty?
  raise UnauthorizedError, "invalid bearer token" unless Rack::Utils.secure_compare(token, @api_key)

  @app.call(env)
end