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
What WWW-Authenticate announces. realm is a label a client may show; it names nothing
about the deployment beyond what the application chose.
Constructors
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).