Authorization¶
Katal provides policy-based authorization through named gates. Policies are evaluated against the current user and the request context.
Defining Policies¶
Policies are registered on the Application. Each policy is a function that receives the authenticated user and the full MiddlewareContext:
type AuthorizationPolicy = (
user: User,
context: MiddlewareContext,
) => boolean | Promise<boolean>;
Single policy¶
app.definePolicy("admin", (user) => user.role === "admin");
Multiple policies at once¶
app.definePolicies({
"admin": (user) => user.role === "admin",
"editor": (user) => ["admin", "editor"].includes(user.role),
"owner": (user, ctx) => String(user.id) === ctx.params.id,
"owner-or-admin": async (user, ctx) => {
return user.role === "admin" || String(user.id) === ctx.params.id;
},
});
Protecting Routes¶
Use the can:<policy-name> middleware key on any route. Always place it after the auth middleware so ctx.user is populated:
router.get("/admin/stats", AdminController, { middleware: ["auth", "can:admin"] });
router.put("/posts/:id", EditPostController, { middleware: ["auth", "can:owner-or-admin"] });
router.delete("/posts/:id", EditPostController, { middleware: ["auth", "can:owner"] });
Error Responses¶
| Scenario | Status | Body |
|---|---|---|
| No authenticated user | 401 | Problem Details |
Policy returns false |
403 | Problem Details |
| Policy not registered | 500 | Problem Details |
All errors use application/problem+json:
{
"type": "https://tools.ietf.org/html/rfc9457",
"title": "Forbidden",
"status": 403,
"detail": "Policy 'admin' denied access."
}
Manual Policy Evaluation¶
Evaluate a policy programmatically inside a controller:
class DeleteCommentController extends Controller {
async handle(ctx: RequestContext) {
const authz = app.resolve<Authorization>("authorization");
const allowed = await authz.can("owner", ctx as unknown as MiddlewareContext);
if (!allowed) return this.error("Forbidden", 403);
await db.deleteComment(ctx.params.id);
return this.success({ deleted: true });
}
}
Recommended Workflow¶
- Register
authmiddleware and protect every restricted route with it. - Define fine-grained policies with
definePolicy/definePolicies. - Add
can:<policy>entries afterauthin each route's middleware array. - Keep policies pure functions — no side effects, easy to unit test.