Learn how to authorize and protect actions, pages, mutations, and more in your application.
Authorization APIs
Using Kilpi, there are several APIs for performing authorizations. They can all be used to protect your application, with slightly different behaviour.
Kilpi.isAuthorized
The simplest way to check for authorizations is to use Kilpi.isAuthorized
. It returns a Promise<boolean>
indicating whether the user is authorized or not. Pass it the policy key and a resource, if the policy requires one.
Unlike with Kilpi.authorize
, you must also handle the unauthorized case manually.
if (await Kilpi.isAuthorized("documents:create")) { await createDocument();} else { // Handle unauthorized here...}
if (await Kilpi.isAuthorized("documents:delete", document)) { await deleteDocument(document.id);} else { // Handle unauthorized here...}
Kilpi.authorize
In some cases, throwing on unauthorized is the simplest way to protect your application, e.g. to automatically always throw a 403 error when an authorization check fails.
Kilpi.authorize
also takes in the policy key and optionally a resource if the policy requires one.
// Both will throw if not authorizedawait Kilpi.authorize("documents:create");await Kilpi.authorize("documents:delete", document);
Handling unauthorized errors
Instead of the default behaviour (throws a KilpiError.AuthorizationDenied
error), setup error handling to automatically handle unauthorized errors.
Setup a global error handler
Start by setting up a global error handler in createKilpi
.
export const Kilpi = createKilpi({ ..., settings: { defaultOnUnauthorized(error) { throw new HTTPException(403, error.message); } }})
// Will throw a 403 error if not authorizedawait Kilpi.authorize("documents:create");
Setup a handler for each request (Optional)
For more control over your error handling, you can setup a handler for each request using Kilpi.onUnauthorized
.
export default async function CreateDocumentPage() { // Any unauthorized error anywhere on this page should redirect to login Kilpi.onUnauthorized((error) => { redirect("/login"); // Redirect via throw, e.g. in Next.js });
// Will redirect to login if not authorized await Kilpi.authorize("documents:create");
// ...}
Kilpi.getAuthorization
Similarly to Kilpi.isAuthorized
, Kilpi.getAuthorization
allows you to manually control your authorizations but returns an authorization object, with more data about the result of the authorization check.
For granted authorizations, it contains the narrowed down subject
. For denied authorizations, it contains the error
message.
const authorization = await Kilpi.getAuthorization("documents:create");
if (authorization.granted) { const user = authorization.subject;} else { console.error("Unauthorized", authorization.error);}
Kilpi.unauthorized
If you want to throw similarly as Kilpi.authorize
and trigger the default or request-level error handler, you can call Kilpi.unauthorized
. It works the same way as a failed Kilpi.authorize
check.
const isAllowed = await Kilpi.isAuthorized("documents:create");if (!isAllowed) { Kilpi.unauthorized("Unauthorized"); // Throws}
// Equivalent toawait Kilpi.authorize("documents:create");
Protecting queries
The above APIs can be used to protect all parts of your application, even queries.
Kilpi however offers an optional Kilpi.query
API for creating protected queries. This API allows for co-locating your queries with the authorization logic. Read more below.
Protected Queries
Learn how to protect queries and mutations in your application.
Introducing the Protected Query Pattern for secure data access
Read the article on protected queries