class

KemalIdentity::Kemal::ErrorHandler

Inherits Kemal::Handler < Kemal::HandlerInterface < HTTP::Handler < Reference < Object

Turns the guard exceptions into responses: 401 and 403.

require! raises NotAuthenticatedError — nobody is signed in, so 401. require_fresh! raises FreshAuthenticationRequiredError — the caller is known and simply has to prove it again, so 403 rather than another 401, which would suggest their session had ended.

Where it goes in the chain

Ahead of anything that can raise, which means ahead of PathGuard and ahead of Kemal::RouteHandler — a route calling env.auth.require! raises from inside the route handler, and this must be outside it to catch that. Registering it immediately before AuthenticationHandler satisfies both.

An application that wants its own behaviour simply does not register this and rescues the two classes itself.

Why there is no return-to parameter

The redirect carries no ?return_to=. Building one from the request and reflecting it back after login is the standard shape of an open redirect, and getting it right means validating that the target is local — a decision that belongs to the application, which knows its own routes. An application that wants it adds it at its login route, where the validation is one line and visible.

Constants

DEFAULT_REALM = "api"

What WWW-Authenticate announces. realm is a label a client may show; it names nothing about the deployment beyond what the application chose.

Constructors

new(login_path : String | Nil = "/login", realm : String = DEFAULT_REALM, app : Application | Nil = nil, api_prefixes : Array(String) = [] of String)

api_prefixes names subtrees that must never be redirected, whatever they sent in Accept:

use KemalIdentity::Kemal::ErrorHandler.new(login_path: "/login", api_prefixes: ["/api"])

This is for the monolith HTTP-02 describes — HTML pages, a same-origin SPA and third-party API clients in one process. Without it the redirect decision is a guess made from the request: Accept: application/json and a bearer credential are read as "an API client is asking", and a client that sends neither — curl with no flags, an HTTP library with no default Accept, anything probing for a 401 before it authenticates — receives 302 Location: /login for a path that serves no HTML at all.

A prefix covers its subtree and only its subtree: /api covers /api/items and not /apiary (PathPrefix).

Source

Instance methods