class KeycloakRack::Middleware

Rack middleware that calls {KeycloakRack::Authenticate} to process a keycloak token.

Upon successful processing, it populates the following values into the rack environment for consumption later down the stack:

Public Class Methods

new(app, **options) click to toggle source

@param [#call] app the next component in the rack middleware stack

Calls superclass method
# File lib/keycloak_rack/middleware.rb, line 18
def initialize(app, **options)
  super(**options)

  @app = app
end

Public Instance Methods

call(env) click to toggle source

Process the rack environment and inject the gem's interfaces into it.

If the authentication is a monadic failure, and {KeycloakRack::Config#halt_on_auth_failure halt_on_auth_failure} is true, then it will short-circuit with {#authentication_failed}.

@param [Hash] env the rack environment @return [Object]

# File lib/keycloak_rack/middleware.rb, line 31
def call(env)
  result = authenticate.call(env)

  return authentication_failed(env, result) if halt?(result)

  session_opts = { skipped: false, auth_result: result }

  case result
  in Success[:authenticated, decoded_token]
    session_opts[:token] = decoded_token
  in Success[:skipped]
    session_opts[:skipped] = true
  else
    # nothing to do
  end

  env["keycloak:session"] = session = KeycloakRack::Session.new(**session_opts)
  env["keycloak:authorize_realm"] = session.authorize_realm
  env["keycloak:authorize_resource"] = session.authorize_resource

  @app.call(env)
end

Private Instance Methods

authentication_failed(env, monad) click to toggle source

Build the authentication failure when short-circuiting.

@note See {#build_failure_headers} and {#build_failure_body} for opportunities

to override.

@param [Hash] env the rack environment @param [Dry::Monads::Result] monad @return [(Integer, { String => String }, <String>)] rack response

# File lib/keycloak_rack/middleware.rb, line 63
def authentication_failed(env, monad)
  status = build_failure_status env, monad

  headers = build_failure_headers env, monad

  body = build_failure_body env, monad

  # :nocov:
  body = body.to_json unless body.kind_of?(String)
  # :nocov:

  [
    status,
    headers,
    [ body ]
  ]
end
build_failure_body(env, monad) click to toggle source

@todo Make customizable @note Currently uses GraphQL error format. @param [Hash] env the rack environment @param [Dry::Monads::Result] monad @return [String, to_json]

# File lib/keycloak_rack/middleware.rb, line 110
def build_failure_body(env, monad)
  _reason, message, _token, _original_error = monad.failure

  {
    errors: [
      {
        message: message,
        extensions: {
          code: "UNAUTHENTICATED"
        }
      }
    ]
  }
end
build_failure_headers(env, monad) click to toggle source

@todo Make customizable @param [Hash] env the rack environment @param [Dry::Monads::Result] monad @return [{ String => String }]

# File lib/keycloak_rack/middleware.rb, line 99
def build_failure_headers(env, monad)
  {
    "Content-Type" => "application/json"
  }
end
build_failure_status(env, monad) click to toggle source
# File lib/keycloak_rack/middleware.rb, line 81
def build_failure_status(env, monad)
  case monad
  in Failure[:no_token, _]
    401
  in Failure[:expired, String, String, Exception]
    403
  in Failure[Symbol, String, String, Exception]
    400
  else
    500
    # nothing to do
  end
end
halt?(result) click to toggle source

@param [Dry::Monads::Result] result

# File lib/keycloak_rack/middleware.rb, line 126
def halt?(result)
  return false unless result.failure?

  config.halt_on_auth_failure?
end