========================
CODE SNIPPETS
========================
TITLE: Install and set up Kilpi for Express
DESCRIPTION: Install Kilpi and set up your Kilpi instance by following the quickstart guide. This snippet shows the basic initialization of the Kilpi instance.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/express.mdx#_snippet_0
LANGUAGE: ts
CODE:
```
export const Kilpi = createKilpi({
getSubject,
policies,
...
})
```
----------------------------------------
TITLE: Create Kilpi Instance
DESCRIPTION: Initial setup for creating the Kilpi instance in `src/kilpi/kilpi.ts`, importing necessary functions like `createKilpi` and `getSubject` to prepare for authorization operations.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/getting-started/quickstart.mdx#_snippet_4
LANGUAGE: ts
CODE:
```
// src/kilpi/kilpi.ts
import { createKilpi } from "@kilpi/core";
import { getSubject } from "./subject";
```
----------------------------------------
TITLE: Install Kilpi Core Package
DESCRIPTION: Instructions to install the Kilpi core package using various JavaScript package managers like npm, yarn, pnpm, and bun.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/getting-started/quickstart.mdx#_snippet_0
LANGUAGE: bash
CODE:
```
npm install @kilpi/core
```
LANGUAGE: bash
CODE:
```
yarn add @kilpi/core
```
LANGUAGE: bash
CODE:
```
pnpm add @kilpi/core
```
LANGUAGE: bash
CODE:
```
bun add @kilpi/core
```
----------------------------------------
TITLE: Install & setup Kilpi
DESCRIPTION: Install Kilpi and setup your Kilpi instance by following the quickstart guide.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/hono.mdx#_snippet_0
LANGUAGE: ts
CODE:
```
export const Kilpi = createKilpi({
getSubject,
policies,
...
})
```
----------------------------------------
TITLE: Run Project Setup
DESCRIPTION: Initializes the project, including `.env` setup, database migration and seeding, dependency installation, and starting the Next.js application. Requires Bun.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/examples/next-rsc-demo/README.md#_snippet_0
LANGUAGE: bash
CODE:
```
bun run bootstrap
```
----------------------------------------
TITLE: Import Policies into Kilpi Instance (Larger Projects)
DESCRIPTION: Example of importing domain-specific policies from separate files into the main Kilpi instance in `kilpi.ts` for larger, more organized projects.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/getting-started/quickstart.mdx#_snippet_1
LANGUAGE: ts
CODE:
```
// kilpi.ts
import { createKilpi } from '@kilpi/core';
import { organizationPolicies } from './policies/organizations.policies';
import { documentPolicies } from './policies/documents.policies';
import { mediaPolicies } from './policies/media.policies';
export const Kilpi = createKilpi({
policies: {
organizations: organizationPolicies,
documents: documentPolicies,
media: mediaPolicies,
},
// ...
})
```
----------------------------------------
TITLE: Protect Function Using Kilpi.authorize
DESCRIPTION: This example shows how to protect a function by using the `Kilpi.authorize` method. It checks for the 'documents:create' permission before executing the function's core logic. Further details on authorization concepts are linked.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/getting-started/quickstart.mdx#_snippet_6
LANGUAGE: TypeScript
CODE:
```
// myFunction.ts
export async function myFunction() {
await Kilpi.authorize("documents:create");
// Your logic here ...
}
```
----------------------------------------
TITLE: Install React Client Plugin
DESCRIPTION: Install the Kilpi React Client plugin using various package managers.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/react-client.mdx#_snippet_0
LANGUAGE: bash
CODE:
```
npm install @kilpi/react-client
```
LANGUAGE: bash
CODE:
```
yarn add @kilpi/react-client
```
LANGUAGE: bash
CODE:
```
pnpm add @kilpi/react-client
```
LANGUAGE: bash
CODE:
```
bun add @kilpi/react-client
```
----------------------------------------
TITLE: Define Kilpi Policies with Type-Checking
DESCRIPTION: Example of defining a `Policyset` in `src/kilpi/policies.ts`. It includes a `documents.read` policy that grants access based on user authentication, document public status, or ownership, demonstrating the use of `grant()` and `deny()` functions.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/getting-started/quickstart.mdx#_snippet_3
LANGUAGE: ts
CODE:
```
// src/kilpi/policies.ts
import { type Policyset, deny, grant } from "@kilpi/core";
import type { Subject } from "./subject";
const policies = {
documents: {
// All authed users can read documents they own or public documents
read(user, document: Document) {
if (!user) return deny("Unauthenticated");
if (document.isPublic) return grant(user);
if (user.id === document.ownerId) return grant(user);
return deny();
}
}
} as const satisfies Policyset
```
----------------------------------------
TITLE: Install Kilpi Client Package
DESCRIPTION: Instructions for installing the @kilpi/client package using various package managers. This package is essential for client-side authorization.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/usage-on-client.mdx#_snippet_0
LANGUAGE: bash
CODE:
```
npm install @kilpi/client
```
LANGUAGE: bash
CODE:
```
yarn add @kilpi/client
```
LANGUAGE: bash
CODE:
```
pnpm add @kilpi/client
```
LANGUAGE: bash
CODE:
```
bun add @kilpi/client
```
----------------------------------------
TITLE: Initialize Kilpi with Policies
DESCRIPTION: This snippet demonstrates how to initialize the Kilpi authorization library by providing a `getSubject` function and a set of `policies`. It also mentions the possibility of configuring plugins and default behaviors.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/getting-started/quickstart.mdx#_snippet_5
LANGUAGE: TypeScript
CODE:
```
import { policies } from "./policies";
export const Kilpi = createKilpi({
getSubject,
policies,
});
```
----------------------------------------
TITLE: Install Kilpi React Server Component Plugin
DESCRIPTION: Instructions for installing the @kilpi/react-server plugin using various package managers like npm, yarn, pnpm, and bun.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/react-server-components.mdx#_snippet_0
LANGUAGE: bash
CODE:
```
npm install @kilpi/react-server
```
LANGUAGE: bash
CODE:
```
yarn add @kilpi/react-server
```
LANGUAGE: bash
CODE:
```
pnpm add @kilpi/react-server
```
LANGUAGE: bash
CODE:
```
bun add @kilpi/react-server
```
----------------------------------------
TITLE: Create and Use Custom Client-Side Kilpi Plugin
DESCRIPTION: Demonstrates how to create a custom client-side plugin using `createKilpiClientPlugin` to extend `KilpiClient`. The example shows an `AuthorizationCounterClientPlugin` with `get()` and `increment()` methods, illustrating plugin instantiation, internal logic, and public interface usage.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/plugin.mdx#_snippet_2
LANGUAGE: ts
CODE:
```
// 1. CREATE A PLUGIN
// Create generic function for instantiating your plugin
export function AuthorizationCounterClientPlugin(
// Allow for configuring the plugin's functionality
options: { initialValue: number }
) {
// All plugins must return `createKilpiClientPlugin(...)`
return createKilpiClientPlugin((KilpiClient: T) => {
// Your plugin logic here: Create state, call hooks, etc.
let count = options.initialValue;
// Return the public interface and methods (or {}) for the plugin.
// The methods should be namespaced, as done here with the
// `KilpiClient.authorizationCounter` namespace.
return {
authorizationCounter: {
// Custom methods
get() {
return count;
},
increment() {
count += 1;
}
}
};
});
}
// 2. USE THE PLUGIN
const KilpiClient = createKilpiClient({
...,
plugins: [
// Apply and configure the plugin
AuthorizationCounterClientPlugin({ initialValue: 10 })
]
});
// Use custom methods if any provided
KilpiClient.authorizationCounter.increment();
KilpiClient.authorizationCounter.get(); // 11
```
----------------------------------------
TITLE: Create and Use Custom Server-Side Kilpi Plugin
DESCRIPTION: Illustrates how to create a custom server-side plugin using `createKilpiPlugin` to extend `KilpiCore`. The example shows an `AuthorizationCounterPlugin` that tracks authorization counts and exposes a `get()` method, demonstrating plugin instantiation, internal logic (using hooks), and public interface usage. It also highlights how to make plugins typesafe.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/plugin.mdx#_snippet_1
LANGUAGE: ts
CODE:
```
// 1. CREATE A PLUGIN
// Create generic function for instantiating your plugin
export function AuthorizationCounterPlugin(
// Allow for configuring the plugin's functionality
options: { initialValue: number }
) {
// All plugins must return `createKilpiPlugin(...)`
return createKilpiPlugin((Kilpi: T) => {
// Your plugin logic here: Create state, call hooks, etc.
let count = options.initialValue;
Kilpi.hooks.onAfterAuthorization(() => {
count += 1;
});
// Return the public interface and methods (or {}) for the plugin.
// The methods should be namespaced, as done here with the
// `Kilpi.authorizationCounter` namespace.
return {
authorizationCounter: {
// Custom method
get() {
return count;
}
}
};
});
}
// 2. USE THE PLUGIN
const Kilpi = createKilpi({
...,
plugins: [
// Apply and configure the plugin
AuthorizationCounterPlugin({ initialValue: 10 })
]
});
// Use custom methods if any provided
Kilpi.authorizationCounter.get();
```
----------------------------------------
TITLE: Install Kilpi React Server Components Plugin
DESCRIPTION: Commands to install the Kilpi React Server Components plugin using various package managers. This plugin helps automatically provide a scope for React Server Components.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/next-js.mdx#_snippet_1
LANGUAGE: bash
CODE:
```
npm install @kilpi/react-server
```
LANGUAGE: bash
CODE:
```
yarn add @kilpi/react-server
```
LANGUAGE: bash
CODE:
```
pnpm add @kilpi/react-server
```
LANGUAGE: bash
CODE:
```
bun add @kilpi/react-server
```
----------------------------------------
TITLE: Initialize Kilpi instance for Oak
DESCRIPTION: This snippet demonstrates the initial setup of the Kilpi authorization library. It shows how to use `createKilpi` to configure your Kilpi instance, requiring a `getSubject` function and defined `policies` for authorization logic.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/oak.mdx#_snippet_0
LANGUAGE: typescript
CODE:
```
export const Kilpi = createKilpi({
getSubject,
policies,
...
})
```
----------------------------------------
TITLE: Define a Basic Kilpi Policy
DESCRIPTION: This example shows how to define a simple policy that takes a `subject` (aliased as `user`) and grants access if the user is authenticated, otherwise denies it. It also includes an example of how to authorize against this policy.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/policy.mdx#_snippet_1
LANGUAGE: TypeScript
CODE:
```
export const policies = {
comments: {
create(user) {
if (!user) return deny("Unauthenticated");
return grant(user);
}
}
} as const satisfies Policyset;
await Kilpi.authorize("comments:create");
```
----------------------------------------
TITLE: Example of Future Datadog Audit Plugin Integration
DESCRIPTION: This snippet illustrates a potential future integration with popular services like Datadog, showing how a specialized plugin could simplify configuration by abstracting the `onFlushEvents` logic.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/audit.mdx#_snippet_2
LANGUAGE: ts
CODE:
```
plugins: [
DatadogAuditPlugin({
apiKey: process.env.DATADOG_API_KEY,
// ...
})
];
```
----------------------------------------
TITLE: Define Kilpi Subject Type and Getter Function
DESCRIPTION: Defines the `Subject` type and `getSubject` asynchronous function in `src/kilpi/subject.ts`. This function wraps an authentication provider to return the current user's session data, which Kilpi uses for authorization.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/getting-started/quickstart.mdx#_snippet_2
LANGUAGE: ts
CODE:
```
// src/kilpi/subject.ts
export type Subject = { id: string, email: string, name: string };
export async function getSubject(): Promise {
const session = await getSession(); // From your auth provider
if (!session) return null;
return { id: session.id, email: session.email, name: session.name };
}
```
----------------------------------------
TITLE: Expose Kilpi Authorization Endpoint in Frameworks
DESCRIPTION: Examples of how to expose the Kilpi authorization endpoint using popular web frameworks like Next.js (App router) and Hono. The createPostEndpoint() function returns a web-standard request-response function.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/usage-on-client.mdx#_snippet_3
LANGUAGE: ts
CODE:
```
// app/api/kilpi/route.ts
export const POST = Kilpi.createPostEndpoint();
```
LANGUAGE: ts
CODE:
```
const endpoint = Kilpi.createPostEndpoint();
app.post('/api/kilpi', async (c) => await endpoint(c.req.raw));
```
----------------------------------------
TITLE: Configuring Kilpi for Centralized Authorization Policies
DESCRIPTION: This snippet demonstrates how to initialize Kilpi, connecting it to your authentication solution and defining centralized authorization policies. It includes an example policy for deleting comments, ensuring only the author can perform the action.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/blog/2025-03-28-how-to-protect-nextjs-server-actions.mdx#_snippet_2
LANGUAGE: ts
CODE:
```
export const Kilpi = createKilpi({
// Connect your authentication solution to Kilpi to reduce coupling
async getSubject() {
return getCurrentUser(); // From your auth solution
},
// Define your authorization policies in a centralized manner
policies: {
...,
comments: {
...,
delete(user, commentId: string) {
return user && user.id === comment.userId
? grant(user)
: deny("Only the author can delete the comment");
}
}
}
})
```
----------------------------------------
TITLE: Setup global unauthorized error handler in Kilpi
DESCRIPTION: Configure a global error handler in `createKilpi` to customize the default behavior when an authorization check fails, for example, to throw a specific HTTP error like 403 instead of the default `KilpiError.AuthorizationDenied`.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/authorizing.mdx#_snippet_2
LANGUAGE: ts
CODE:
```
export const Kilpi = createKilpi({
...,
settings: {
defaultOnUnauthorized(error) {
throw new HTTPException(403, error.message);
}
}
})
// Will throw a 403 error if not authorized
await Kilpi.authorize("documents:create");
```
----------------------------------------
TITLE: Apply Kilpi.filter in Protector (Inefficient Example)
DESCRIPTION: Presents an inefficient way to use `Kilpi.filter` within a protector. This approach fetches all documents from the database first and then filters them, which can lead to significant performance issues, especially with large datasets.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/protected-queries.mdx#_snippet_13
LANGUAGE: ts
CODE:
```
export const listDocuments = Kilpi.query(
// Fetch all documents
async () => await db.documents.list(),
{
// Return only authorized documents
async protector({ output: documents, subject }) {
return Kilpi.filter("documents:read", documents);
}
}
);
```
----------------------------------------
TITLE: Fetch and Provide Extra Data with Subject
DESCRIPTION: This example illustrates how to extend the subject object with additional data like permissions, memberships, or subscriptions, especially if this data is frequently used across most policies. It uses caching for performance.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/subject.mdx#_snippet_3
LANGUAGE: ts
CODE:
```
// subject.ts
const getSubject = cache(() => {
const user = await getUserFromMyAuthProvider();
if (!user) return null;
return Object.assign(user, {
permissions: await db.getPermissions(user.id),
memberships: await db.getMemberships(user.id),
subscription: await db.getSubscription(user.id),
});
});
```
----------------------------------------
TITLE: Apply AuditPlugin to Kilpi Configuration
DESCRIPTION: Integrate the AuditPlugin into your Kilpi application by adding it to the `plugins` array during `createKilpi` initialization. This enables the plugin to start receiving and processing authorization events.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/audit.mdx#_snippet_0
LANGUAGE: ts
CODE:
```
// kilpi.ts
import { createKilpi, AuditPlugin } from "@kilpi/core"
export const Kilpi = createKilpi({
getSubject,
policies,
plugins: [AuditPlugin({ ... })]
})
```
----------------------------------------
TITLE: Define Policies with Kilpi Policyset
DESCRIPTION: This snippet demonstrates the basic structure for defining policies using the `policies` object and `Policyset` type for type-safety. It includes an example of a nested policy for reading documents, allowing access to authenticated users.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/policy.mdx#_snippet_0
LANGUAGE: TypeScript
CODE:
```
import { deny, grant, type Policyset } from "@kilpi/core";
export const policies = {
// Example hierarchy
documents: {
// Allow all authed members to read docs
read(user) {
if (!user) return deny("Unauthenticated");
return grant(user);
}
}
} as const satisfies Policyset;
```
----------------------------------------
TITLE: Define and Get Subject in Kilpi
DESCRIPTION: This snippet defines the `Subject` type and provides an asynchronous function `getSubject` to retrieve the current subject (user) from an authentication provider. It then shows how to initialize Kilpi with this `getSubject` function.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/subject.mdx#_snippet_0
LANGUAGE: ts
CODE:
```
// src/kilpi/subject.ts
export type Subject = { id: string };
export async function getSubject(): Promise {
const session = await getSession(); // From your auth provider
if (!session) return null;
return { id: session.id };
}
export const Kilpi = createKilpi({ getSubject, ... })
```
----------------------------------------
TITLE: Define and Use a Kilpi Protected Query in TypeScript
DESCRIPTION: This TypeScript example demonstrates how to define a protected query using `Kilpi.query`. It combines a data retrieval function (fetching a document by ID) with a protector function that authorizes access. The protector checks if a document exists and then calls `Kilpi.authorize`. The example also shows how to call the protected query with (`.protect`) or without (`.unsafe`) the protector, and highlights a type error for direct calls to enforce intent.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/protected-queries.mdx#_snippet_0
LANGUAGE: typescript
CODE:
```
const getDocument = Kilpi.query(
async (id: string) => {
return await db.documents.get(id);
},
{
async protector({ input: [id], output: document, subject }) {
if (document) await Kilpi.authorize("documents:read", document);
return document;
},
},
);
await getDocument.protect("123"); // Call with protector
await getDocument.unsafe("123"); // Or skip the protector
await getDocument("123"); // Type error (to enforce communicating intent)
```
----------------------------------------
TITLE: Apply Kilpi.filter in Protector (Efficient Example)
DESCRIPTION: Demonstrates an efficient method for applying `Kilpi.filter` by pre-filtering data at the query level. This ensures that only relevant documents are fetched from the database initially, significantly improving performance by reducing the amount of data processed by the protector.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/protected-queries.mdx#_snippet_14
LANGUAGE: ts
CODE:
```
export const listDocuments = Kilpi.query(
// Fetch only user's documents
async (userId: string) =>
await db.documents.list({
where: { userId: userId }
}),
{
// Ensure authorized to the documents
async protector({ output: documents, subject }) {
return Kilpi.filter("documents:read", documents);
}
}
);
```
----------------------------------------
TITLE: Logging Kilpi Authorization Decisions with Hooks vs. AuditPlugin
DESCRIPTION: Compares two methods for logging authorization decisions in Kilpi: using the `AuditPlugin` with an `onFlushEvents` callback (less recommended for general logging) versus using the `Kilpi.hooks.onAfterAuthorization` hook (preferred for its simplicity and directness). The example highlights the cleaner approach with hooks for logging.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/audit.mdx#_snippet_12
LANGUAGE: typescript
CODE:
```
// kilpi.ts
const Kilpi = createKilpi({
...,
- // Logging using AuditPlugin
- plugins: [
- AuditPlugin({
- strategy: "immediate",
- async onFlushEvents(events) {
- for (const e of events) {
- console.log(format(e))
- }
- }
- }),
- ]
});
+ // Logging using hooks
+ Kilpi.hooks.onAfterAuthorization((e) => console.log(format(e)));
```
----------------------------------------
TITLE: Define an Asynchronous Kilpi Policy with Data Fetching
DESCRIPTION: This example demonstrates how policies can be asynchronous and fetch external data during evaluation. The `archive` policy for comments makes an asynchronous call to `getAiResponse` to decide whether to grant or deny access.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/policy.mdx#_snippet_3
LANGUAGE: TypeScript
CODE:
```
export const policies = {
comments: {
async archive(user, comment: Comment) {
if (!user) return deny("Unauthenticated");
const response = await getAiResponse("Allow deleting comment: Yes or no?");
return response.includes("Yes") ? grant(user) : deny();
}
}
} as const satisfies Policyset;
await Kilpi.authorize("comments:delete", myComment);
```
----------------------------------------
TITLE: Configure AuditPlugin with Batch Strategy
DESCRIPTION: The recommended 'batch' strategy collects events into a batch. When an event is received, a new batch starts, and subsequent events are added until `batchTimeoutSeconds` expires, at which point the batch is flushed. You can also manually flush the batch.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/audit.mdx#_snippet_3
LANGUAGE: ts
CODE:
```
AuditPlugin({
strategy: "batch",
batchTimeoutSeconds: 5,
async onFlushEvents(events) { ... }
})
```
----------------------------------------
TITLE: Use Kilpi Access Component in React Server Components
DESCRIPTION: Example of using the `Access` component provided by the Kilpi React Server Components plugin to protect UI elements. It checks for authorization based on a specified permission and renders fallback content if unauthorized.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/next-js.mdx#_snippet_3
LANGUAGE: tsx
CODE:
```
export const { Access } = Kilpi.ReactServer.createComponents();
Not authorized to create documents
}
>
```
----------------------------------------
TITLE: Authorizing Server Actions with Kilpi
DESCRIPTION: This example shows how to refactor a server action to use Kilpi for authorization. It replaces manual authentication and authorization checks with a single, cleaner `Kilpi.authorize` call, simplifying the action's logic.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/blog/2025-03-28-how-to-protect-nextjs-server-actions.mdx#_snippet_3
LANGUAGE: ts
CODE:
```
"use server";
export async function deleteComment(id: string) {
- // 1. Authentication
- const user = await getCurrentUser(); // From your auth solution
-
- // 2. Authorization: Only allow the user to delete their own comments
- if (!user || user.id !== comment.userId) {
- throw new Error("You are not authorized to delete this comment");
- }
+ await Kilpi.authorize("comments:delete", id);
await sql`DELETE FROM comments WHERE id = ${id}`;
}
```
----------------------------------------
TITLE: Manually Call Kilpi API Endpoint with SuperJSON
DESCRIPTION: This function provides a basic example of manually calling the Kilpi API endpoint. It demonstrates setting up a POST request with 'Content-Type' and 'Authorization' headers, and using 'SuperJSON.stringify' to serialize the request body before sending.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/endpoint.mdx#_snippet_3
LANGUAGE: ts
CODE:
```
import * as SuperJSON from "superjson";
function callKilpiApiEndpoint(body: RequestBody) {
const response = await fetch(process.env.PUBLIC_KILPI_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.PUBLIC_KILPI_SECRET}`,
},
body: SuperJSON.stringify(body),
});
if (!response.ok) throw new Error("Failed to call Kilpi API");
return (await response.json()) as ResponseBody;
}
```
----------------------------------------
TITLE: Authorize actions using Kilpi
DESCRIPTION: This example demonstrates how to authorize an action, such as 'documents:update', by calling `Kilpi.authorize`. It returns the authorized subject (user) if the action is permitted based on the defined policies.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/packages/core/README.md#_snippet_1
LANGUAGE: TypeScript
CODE:
```
const user = await Kilpi.authorize("documents:update", document);
```
----------------------------------------
TITLE: Setup per-request unauthorized error handler with Kilpi.onUnauthorized
DESCRIPTION: For more granular control, set up an error handler for each request using `Kilpi.onUnauthorized`. This handler is stored in the current scope and allows custom actions like redirecting on unauthorized access, providing flexible error management.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/authorizing.mdx#_snippet_3
LANGUAGE: ts
CODE:
```
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");
// ...
}
```
----------------------------------------
TITLE: Implementing simple authorization in a Next.js server action
DESCRIPTION: This example extends the basic server action to include a simple authorization check. It illustrates how to authenticate a user (placeholder `getCurrentUser()`) and then verify if they are authorized to perform the action (e.g., deleting their own comment), throwing an error if unauthorized. The diff format highlights the added lines.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/blog/2025-03-28-how-to-protect-nextjs-server-actions.mdx#_snippet_1
LANGUAGE: ts
CODE:
```
"use server";
export async function deleteComment(id: string) {
+ // 1. Authentication
+ const user = await getCurrentUser(); // From your auth solution
+
+ // 2. Authorization: Only allow the user to delete their own comments
+ if (!user || user.id !== comment.userId) {
+ throw new Error("You are not authorized to delete this comment");
+ }
await sql`DELETE FROM comments WHERE id = ${id}`;
}
```
----------------------------------------
TITLE: Filter AuditPlugin Events Based on Criteria
DESCRIPTION: Use the `filterEvents` option to selectively include or exclude events from being sent. This example demonstrates how to filter events to only include those where authorization was denied.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/audit.mdx#_snippet_8
LANGUAGE: ts
CODE:
```
AuditPlugin({
...,
filterEvents(event) {
return !event.authorization.granted
}
})
```
----------------------------------------
TITLE: Protecting UI with Kilpi's Access Component in React Server Components
DESCRIPTION: This example demonstrates how to use the component from the @kilpi/react-server plugin to conditionally render UI based on authorization policies within React Server Components. It shows how to reference a policy, provide a resource, and optionally customize the UI for unauthorized or loading states.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/protecting-ui.mdx#_snippet_0
LANGUAGE: tsx
CODE:
```
export async function Document({ id }) {
const doc = await getDocument.protect(id);
return (
{doc.title}
Not allowed to delete button}
Loading={
Loading...
}
>
);
}
```
----------------------------------------
TITLE: Integrate Kilpi Endpoint with Next.js API Route
DESCRIPTION: This example shows how to expose the Kilpi endpoint as a POST handler in a Next.js API route. The 'Kilpi.createPostEndpoint()' function constructs a web-standard request-response handler suitable for direct assignment to the 'POST' export.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/endpoint.mdx#_snippet_1
LANGUAGE: ts
CODE:
```
// app/api/kilpi/route.ts
// (req: Request) => Promise
export const POST = Kilpi.createPostEndpoint();
```
----------------------------------------
TITLE: Integrate Kilpi Scope with next-safe-action Middleware
DESCRIPTION: This example shows how to use `Kilpi.runInScope` within a `next-safe-action` middleware. It ensures that all actions processed by the client run within a Kilpi scope, allowing for global authorization error handling and consistent authorization checks.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/next-js.mdx#_snippet_6
LANGUAGE: typescript
CODE:
```
export const ActionClient = createSafeActionClient({ ... })
.use(async ({ next }) => {
return await Kilpi.runInScope(async () => {
// Optionally setup a global authorization error handler here
Kilpi.onUnauthorized((error) => { ... });
// Run the action within the scope
return await next();
});
});
```
----------------------------------------
TITLE: Validating Server Action Data with next-safe-action and Zod
DESCRIPTION: This example demonstrates how to integrate `next-safe-action` and `zod` to validate incoming data for server actions. It shows how to define a schema for input validation and combine it with Kilpi authorization for a secure and robust server action.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/blog/2025-03-28-how-to-protect-nextjs-server-actions.mdx#_snippet_5
LANGUAGE: ts
CODE:
```
"use server";
import { actionClient } from "./next-safe-action-client";
import { z } from "zod";
import { Kilpi } from "./kilpi";
export const deleteComment = actionClient
.schema(z.object({ commentId: z.string() }))
.action(async ({ parsedInput: { commentId } }) => {
await Kilpi.authorize("comments:delete", commentId);
await sql`DELETE FROM comments WHERE id = ${commentId}`;
})
```
----------------------------------------
TITLE: Protecting UI with Kilpi's ClientAccess Component in React Client Components
DESCRIPTION: This example illustrates the use of the component from the @kilpi/react-client plugin to protect UI elements in React Client Components. It functions similarly to the server component, allowing policy and resource specification, along with custom UI for unauthorized or loading states.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/protecting-ui.mdx#_snippet_1
LANGUAGE: tsx
CODE:
```
export function Document({ doc }) {
return (
{doc.title}
Not allowed to delete button}
Loading={
Loading...
}
>
);
}
```
----------------------------------------
TITLE: Get detailed authorization result with Kilpi.getAuthorization
DESCRIPTION: Similar to `Kilpi.isAuthorized`, this method allows manual control over authorizations but returns an authorization object with more detailed data. For granted authorizations, it includes the narrowed-down subject; for denied ones, it provides the error message.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/authorizing.mdx#_snippet_6
LANGUAGE: ts
CODE:
```
const authorization = await Kilpi.getAuthorization("documents:create");
if (authorization.granted) {
const user = authorization.subject;
} else {
console.error("Unauthorized", authorization.error);
}
```
----------------------------------------
TITLE: Kilpi Policy Subject Type Narrowing
DESCRIPTION: This example demonstrates how TypeScript automatically narrows down the subject type when defining policies with `Policyset`. It shows that after a successful authorization check with `Kilpi.authorize`, the `user` variable is inferred to be non-null, reflecting the type-safety benefits.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/policy.mdx#_snippet_5
LANGUAGE: TypeScript
CODE:
```
export const policies = {
documents: {
read(user) {
if (!user) return deny("Unauthenticated");
return grant(user);
}
}
} as const satisfies Policyset<{ userId: string } | null>;
// User is inferred to be non-null
const user = await Kilpi.authorize("documents:read");
// ^? { userId: string }
```
----------------------------------------
TITLE: Define a Kilpi Policy with a Resource
DESCRIPTION: This snippet illustrates how to define a policy that accepts a `resource` as a second argument. The example policy for deleting comments checks if the user is authenticated and if the comment's `userId` matches the authenticated user's `id` before granting access.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/policy.mdx#_snippet_2
LANGUAGE: TypeScript
CODE:
```
export const policies = {
comments: {
delete(user, comment: Comment) {
if (!user) return deny("Unauthenticated");
return comment.userId === user.id ? grant(user) : deny();
}
}
} as const satisfies Policyset;
await Kilpi.authorize("comments:delete", myComment);
```
----------------------------------------
TITLE: Authorize Actions with Kilpi in TypeScript
DESCRIPTION: This example shows how to perform an authorization check with a single line of code using the `Kilpi.authorize` method. It attempts to authorize the "documents:update" action against a specific document, returning the authorized user if successful or throwing an error if authorization fails.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/packages/client/README.md#_snippet_1
LANGUAGE: TypeScript
CODE:
```
const user = await Kilpi.authorize("documents:update", document);
```
----------------------------------------
TITLE: Provide Request Scope with Kilpi Middleware in Nest.js
DESCRIPTION: To enable Kilpi's full feature set, a request scope must be provided. This example shows an Express-style middleware using `Kilpi.runInScope` to wrap the request processing, ensuring the scope is available for authorization checks.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/nest-js.mdx#_snippet_1
LANGUAGE: typescript
CODE:
```
import { Request, Response, NextFunction } from 'express';
function kilpiMiddleware(req: Request, res: Response, next: NextFunction) {
Kilpi.runInScope(async () => {
next();
})
};
app.use(kilpiMiddleware)
```
----------------------------------------
TITLE: Display Error Messages in UI using useSafeAction
DESCRIPTION: Demonstrates how to use the `useSafeAction` hook in a React client component to trigger a server action. This setup implicitly handles and prepares error messages (e.g., for a toast notification, though the toast implementation itself is not shown).
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/blog/2025-03-28-how-to-protect-nextjs-server-actions.mdx#_snippet_9
LANGUAGE: tsx
CODE:
```
"use client";
export function DeleteCommentButton({ commentId }: { commentId: string }) {
const action = useSafeAction(deleteCommentAction);
return (
);
}
```
----------------------------------------
TITLE: Handle unauthorized Kilpi errors in Oak
DESCRIPTION: This example shows how to gracefully handle unauthorized access errors from Kilpi within an Oak application. By using `Kilpi.onUnauthorized`, you can intercept authorization failures and respond with an HTTP 403 Forbidden status using Oak's `ctx.throw` method.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/oak.mdx#_snippet_2
LANGUAGE: typescript
CODE:
```
app.use(async (ctx, next) => {
Kilpi.runInScope(async () => {
Kilpi.onUnauthorized(({ message }) => ctx.throw(403, message));
await next();
})
})
```
----------------------------------------
TITLE: Component Usage with Request-Level Unauthorized Handling
DESCRIPTION: Shows a React component leveraging `Kilpi.onUnauthorized` to define a request-level error handler. This setup allows subsequent calls to `.protect()` on queries to proceed without explicit unauthorized checks, centralizing error management.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/protected-queries.mdx#_snippet_21
LANGUAGE: tsx
CODE:
```
export async function Page({ id }) {
// Specify request level error handler (or use global default error handler)
Kilpi.onUnauthorized((error) => redirect(`/login?message=${error.message}`));
// No need to handle unauthorized errors
const document = await getDocument.protect(id);
const comments = await getComments.protect(document.id);
// Children can also call .protect() without handling unauthorized cases
return ;
}
```
----------------------------------------
TITLE: Ambiguous Endpoint Safety with Implicit Authorization
DESCRIPTION: Presents an example of an API endpoint where the safety of the `getDocument` call is not immediately apparent. Without explicit authorization, it's unclear whether the data returned is properly secured, highlighting a potential risk in collaborative development.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/protected-queries.mdx#_snippet_5
LANGUAGE: ts
CODE:
```
app.get("/documents/:id", async (req, res) => {
const document = await getDocument(req.params.id);
if (!document) return res.status(404).send("Not found");
return res.send(document);
});
```
----------------------------------------
TITLE: Creating a Kilpi instance
DESCRIPTION: Shows how to initialize the Kilpi instance by importing `createKilpi` and providing the defined policies and the `getSubject` function. This centralizes the authorization logic and makes it ready for use across the application.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/blog/2025-03-24-introducing-kilpi.mdx#_snippet_4
LANGUAGE: ts
CODE:
```
// kilpi.ts
import { createKilpi } from "@kilpi/core";
import { policies } from "./policies";
import { getSubject } from "./subject";
export const Kilpi = createKilpi({ policies, getSubject });
```
----------------------------------------
TITLE: Migrate RBAC Policy to ABAC for Granular Control
DESCRIPTION: Shows how to extend an existing RBAC policy to incorporate Attribute-Based Access Control (ABAC) logic. This example demonstrates breaking out of the strict RBAC model to allow for more granular access control, such as enabling users to delete documents they own, in addition to administrators.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/advanced/rbac.mdx#_snippet_2
LANGUAGE: ts
CODE:
```
export const policies = {
documents: {
read: rbacPolicy("user", "admin", "guest"),
create: rbacPolicy("user", "admin"),
- delete: rbacPolicy("admin"),
+ delete((user, document: Document) {
+ return user && (hasRole(user, "admin") || user.id === document.ownerId) ? grant(user) : deny()
+ })
},
}
+ function hasRole(subject: Subject, ...roles: Role[]) {
+ return roles.some(role => subject.roles.includes(role));
+ }
```
----------------------------------------
TITLE: Initialize Kilpi instance for Koa
DESCRIPTION: Demonstrates how to initialize a Kilpi instance by providing `getSubject` and `policies` configurations, essential for setting up authorization.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/koa.mdx#_snippet_0
LANGUAGE: ts
CODE:
```
export const Kilpi = createKilpi({
getSubject,
policies,
...
})
```
----------------------------------------
TITLE: Initialize KilpiClient Instance
DESCRIPTION: Shows how to create a KilpiClient instance using createKilpiClient, connecting it to the server-side Kilpi endpoint. It optionally demonstrates type inference for improved type safety.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/usage-on-client.mdx#_snippet_4
LANGUAGE: ts
CODE:
```
// kilpi-client.ts
import type { Kilpi } from "./kilpi.ts";
export const KilpiClient = createKilpiClient({
infer: {} as typeof Kilpi, // Infer subject and policies from server instance
connect: {
endpointUrl: process.env.PUBLIC_KILPI_URL,
secret: process.env.PUBLIC_KILPI_SECRET,
},
});
```
----------------------------------------
TITLE: Configure Kilpi Instance in Next.js
DESCRIPTION: This snippet shows the basic configuration for initializing Kilpi in a Next.js project. It requires `getSubject` and `policies` to be defined for your authorization logic.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/next-js.mdx#_snippet_0
LANGUAGE: typescript
CODE:
```
export const Kilpi = createKilpi({
getSubject,
policies,
...
})
```
----------------------------------------
TITLE: Simplified authorization with Kilpi
DESCRIPTION: Demonstrates how Kilpi simplifies authorization checks. Instead of complex inline conditions, a single `Kilpi.authorize` call handles the logic, making the `deleteDocument` function cleaner and more maintainable by centralizing authorization rules.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/blog/2025-03-24-introducing-kilpi.mdx#_snippet_1
LANGUAGE: ts
CODE:
```
async function deleteDocument(id: string) {
const document = await db.getDocument(id);
await Kilpi.authorize("documents:delete", document);
await db.deleteDocument(id);
}
```
----------------------------------------
TITLE: Fetching Authorization Decisions with KilpiClient
DESCRIPTION: Demonstrates how to use `KilpiClient.fetchIsAuthorized` to check authorization for policies, both with and without a specific resource. It highlights the use of `key` for policy identification and `resource` for resource-specific checks, with optional `queryOptions`.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/usage-on-client.mdx#_snippet_6
LANGUAGE: typescript
CODE:
```
// Fetch authorization decisions for policy without resource
const canCreateDocument = await KilpiClient.fetchIsAuthorized({
key: "documents:create",
queryOptions: { ... } // Optional
});
// Fetch authorization decisions for policy with resource
const canUpdateComment = await KilpiClient.fetchIsAuthorized({
key: "comments:update",
resource: comment,
queryOptions: { ... } // Optional
});
```
----------------------------------------
TITLE: Integrating Kilpi Client with React
DESCRIPTION: Shows how to integrate `@kilpi/client` with React using the `@kilpi/react-client` package. It demonstrates initializing the client with `ReactClientComponentPlugin` and utilizing `useSubject`, `useIsAuthorized` hooks, and the `ClientAccess` component for conditional rendering based on authorization policies.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/usage-on-client.mdx#_snippet_8
LANGUAGE: tsx
CODE:
```
const KilpiClient = createKilpiClient({
// ...,
plugins: [ReactClientComponentPlugin()],
});
const {
ClientAccess,
useSubject,
useIsAuthorized
} = KilpiClient.ReactClient.createComponents()
function Component() {
const subject = useSubject();
const { isAuthorized: canCreateDocuments } = useIsAuthorized("documents:create");
return (
<>
{/* Option 1 */}
{canCreateDocument && }
{/* Option 2 */}
You are not authorized to create this document}
>
You are authorized to create this document
>
)
}
```
----------------------------------------
TITLE: Fetch Subject with KilpiClient
DESCRIPTION: Illustrates how to use the KilpiClient to fetch the current subject's authorization decisions. It also shows how to pass optional query options like an abort signal.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/usage-on-client.mdx#_snippet_5
LANGUAGE: ts
CODE:
```
// Fetch the subject
const subject = await KilpiClient.fetchSubject();
// Or optionally with query options
const subject = await KilpiClient.fetchSubject({
queryOptions: { signal },
});
```
----------------------------------------
TITLE: Initialize Kilpi Instance in Nest.js
DESCRIPTION: This snippet demonstrates how to initialize a Kilpi instance by calling `createKilpi`. It requires providing functions for `getSubject` and `policies` to define authorization logic.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/nest-js.mdx#_snippet_0
LANGUAGE: typescript
CODE:
```
export const Kilpi = createKilpi({
getSubject,
policies,
...
})
```
----------------------------------------
TITLE: Protecting Actions with Kilpi's Expressive API (TypeScript)
DESCRIPTION: This snippet demonstrates Kilpi's short and expressive API for protecting actions. It shows how to protect a document retrieval operation and then authorize an update action on that document using a policy key like 'documents:update'. This highlights Kilpi's aim for single-line protection and its server-side authorization approach.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/getting-started/introduction.mdx#_snippet_0
LANGUAGE: ts
CODE:
```
const doc = await getDocument.protect("1");
Kilpi.authorize("documents:update", doc);
```
----------------------------------------
TITLE: Apply Kilpi React Server Components Plugin Configuration
DESCRIPTION: This snippet demonstrates how to integrate the React Server Components plugin into your existing Kilpi configuration by adding it to the `plugins` array. This enables automatic scope provision for RSCs.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/installation/next-js.mdx#_snippet_2
LANGUAGE: typescript
CODE:
```
// kilpi.ts
export const Kilpi = createKilpi({
getSubject,
policies,
+ plugins: [ReactServerComponentPlugin()]
})
```
----------------------------------------
TITLE: Create Kilpi React Client Components and Hooks
DESCRIPTION: Use `Kilpi.ReactClient.createComponents` to generate utility hooks (`useIsAuthorized`, `useSubject`) and a component (`ClientAccess`) for interacting with the KilpiClient.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/react-client.mdx#_snippet_2
LANGUAGE: ts
CODE:
```
// kilpi.components.ts
export const {
// Utility hooks that wrap KilpiClient methods
useIsAuthorized,
useSubject,
// Utility component that wraps useIsAuthorized
ClientAccess,
} = KilpiClient.ReactClient.createComponents();
```
----------------------------------------
TITLE: Setting up a Kilpi subject
DESCRIPTION: Defines the `Subject` type and the `getSubject` asynchronous function. This function is responsible for retrieving user session information from an authentication provider and transforming it into a `Subject` object for Kilpi to use in authorization policies.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/blog/2025-03-24-introducing-kilpi.mdx#_snippet_2
LANGUAGE: ts
CODE:
```
// subject.ts
import { getSessionFromMyAuthProvider } from "./my-auth-provider";
export type Subject = { id: string, name: string }
export async function getSubject() {
const session = await getSessionFromMyAuthProvider();
if (!session) return null;
return { id: session.userId, name: session.userName };
}
```
----------------------------------------
TITLE: Configure Kilpi with Endpoint Plugin
DESCRIPTION: This snippet demonstrates how to integrate the EndpointPlugin into your Kilpi configuration. It shows the necessary import and how to add the plugin to the 'plugins' array of the 'createKilpi' function, including a secret for security.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/endpoint.mdx#_snippet_0
LANGUAGE: ts
CODE:
```
// kilpi.ts
import { createKilpi, EndpointPlugin } from "@kilpi/core";
export const Kilpi = createKilpi({
getSubject,
policies,
plugins: [EndpointPlugin({ secret: process.env.KILPI_SECRET })]
})
```
----------------------------------------
TITLE: Using Utility Functions in Kilpi Policies
DESCRIPTION: This snippet shows how to improve policy readability and reusability by using utility functions for common checks like authentication status, admin roles, or membership verification. It refactors a `documents:create` policy to use `unauthed`, `isAdmin`, and `isMember` helper functions.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/policy.mdx#_snippet_4
LANGUAGE: TypeScript
CODE:
```
const unauthed = () => deny("Unauthenticated");
const isAdmin = (subject: Subject) => subject.role === "admin";
const isMember = async (subject: Subject, orgId: string) => {
const memberships = await db.getMembershipsForUser(subject.id);
return memberships.some(m => m.orgId === orgId && m.role === role);
};
export const policies = {
documents: {
async create(user, orgId: string) {
if (!user) return unauthed();
return isAdmin(user) || (await isMember(user, orgId)) ? grant(user) : deny();
}
}
} as const satisfies Policyset;
```
----------------------------------------
TITLE: Configure Public Kilpi Environment Variables
DESCRIPTION: Defines the necessary public environment variables for the Kilpi client, including the server endpoint URL and a secret. These variables ensure the client can securely connect to the server.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/usage-on-client.mdx#_snippet_1
LANGUAGE: bash
CODE:
```
PUBLIC_KILPI_URL=http://localhost:3000/api/kilpi
PUBLIC_KILPI_SECRET=generate-secret
```
----------------------------------------
TITLE: Defining a Basic Protected Query with Kilpi.query
DESCRIPTION: Illustrates the fundamental step of creating a protected query by wrapping any asynchronous function with `Kilpi.query`. This establishes the query as a Kilpi-managed operation, ready for optional protector integration.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/protected-queries.mdx#_snippet_16
LANGUAGE: ts
CODE:
```
export const getDocument = Kilpi.query(
// Any asynchronous function
async (id: string) => await db.documents.get(id),
);
```
----------------------------------------
TITLE: Protect data queries with Kilpi
DESCRIPTION: Shows how to use Kilpi's `query` method to wrap data retrieval functions with authorization logic. The `protector` function ensures that the retrieved document is authorized before being returned, preventing unauthorized data access and simplifying secure data fetching.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/README.md#_snippet_2
LANGUAGE: ts
CODE:
```
const getDocument = Kilpi.query(
async (id: string) => await db.documents.get(id),
{
async protector({ output: doc }) {
if (doc) await Kilpi.authorize("documents:read", doc);
return doc;
}
}
);
await getDocument.protect("1");
```
----------------------------------------
TITLE: Configure AuditPlugin for Serverless Environments with waitUntil
DESCRIPTION: For serverless environments, pass a `waitUntil` function (e.g., from Vercel or Cloudflare) to the plugin. This ensures that any pending batch operations have time to complete before the serverless function terminates.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/audit.mdx#_snippet_7
LANGUAGE: ts
CODE:
```
import { waitUntil } from "@vercel/functions";
AuditPlugin({
strategy: "batch",
batchTimeoutSeconds: 5,
waitUntil, // (promise: Promise) => void
async onFlushEvents(events) { ... }
})
```
----------------------------------------
TITLE: Create Kilpi React Server Components Bindings
DESCRIPTION: Illustrates how to use Kilpi.ReactServer.createComponents to generate React server component bindings, such as the component, for authorization within RSCs. This function provides a structured way to expose Kilpi's UI components.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/react-server-components.mdx#_snippet_3
LANGUAGE: ts
CODE:
```
// kilpi.components.ts
export const { Access } = Kilpi.ReactServer.createComponents();
```
----------------------------------------
TITLE: Apply React Client Plugin in Kilpi Configuration
DESCRIPTION: Configure the Kilpi client to include the React Client Component Plugin, enabling its components and hooks.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/react-client.mdx#_snippet_1
LANGUAGE: ts
CODE:
```
// kilpi.ts
import { createKilpiClient } from "@kilpi/client";
import { ReactClientComponentPlugin } from "@kilpi/react-client";
export const KilpiClient = createKilpiClient({
// ...
plugins: [ReactClientComponentPlugin()]
})
```
----------------------------------------
TITLE: Refactoring Component with Protected Query Pattern
DESCRIPTION: This TypeScript diff illustrates the benefits of using the protected query pattern. It shows how a component's authorization logic can be significantly simplified and made more expressive by replacing manual user checks and redirects with a single call to `getDocument.protect(id)`, leveraging the authorization logic encapsulated within the protected query.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/blog/2025-03-27-introducing-the-protected-query-pattern.mdx#_snippet_3
LANGUAGE: ts
CODE:
```
export async function DocumentPage({ id }) {
- const user = await getCurrentUser();
- if (!user) {
- redirect("/login")
- };
-
- const document = await getDocument(id);
- if (!document) {
- redirect("/documents")
- };
- if (!(user.isAdmin || document.authorId === user.id)) {
- forbidden();
- }
+ const document = await getDocument.protect(id);
+ if (!document) redirect("/documents");
return
{document.title}
}
```
----------------------------------------
TITLE: Implementing a Protector with Centralized Policy
DESCRIPTION: This snippet shows how to implement a `protector` function for a Kilpi query (`getDocument`) that leverages a centralized authorization policy. It uses `Kilpi.authorize("documents:read", document)` to apply the previously defined policy, ensuring consistent authorization logic.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/blog/2025-03-27-introducing-the-protected-query-pattern.mdx#_snippet_10
LANGUAGE: ts
CODE:
```
export const getDocument = Kilpi.query(
cache((id: string) => db.documents.get(id)),
{
async protector({ output: document }) {
if (document) await Kilpi.authorize("documents:read", document);
return document;
},
},
);
```
----------------------------------------
TITLE: Centralized Unauthorized Logic Handling with Kilpi
DESCRIPTION: Demonstrates how `Kilpi` allows centralizing unauthorized logic, such as redirecting to a login page, using a `defaultOnUnauthorized` setting. This significantly reduces code duplication and simplifies error handling across the application by providing a single point of failure management.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/protected-queries.mdx#_snippet_9
LANGUAGE: ts
CODE:
```
// Option 1. Use default error handler
export const Kilpi = await createKilpi({
// ...
settings: {
defaultOnUnauthorized(error) {
redirect(`/login?message=${error.message}`);
},
},
});
```
----------------------------------------
TITLE: Using the Kilpi React Server Component
DESCRIPTION: Demonstrates the usage of the asynchronous component to conditionally render UI based on user authorizations. It supports resource-based policies via the on prop, and provides optional Loading and Unauthorized fallbacks for a better user experience during streaming or denied access.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/plugins/react-server-components.mdx#_snippet_4
LANGUAGE: tsx
CODE:
```
async function Component({ comment }) {
return (
{comment.content}
Loading...}
Unauthorized={
Unauthorized
}
>
);
}
```
----------------------------------------
TITLE: Protector Design: Throwing on Unauthorized with Kilpi.isAuthorized
DESCRIPTION: Demonstrates a protector design where `Kilpi.isAuthorized` is used to check permissions. If authorization fails, the protector returns `null`, relying on a pre-configured `Kilpi.onUnauthorized` error handler (either global or request-level) to manage the unauthorized state.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/protected-queries.mdx#_snippet_19
LANGUAGE: ts
CODE:
```
Kilpi.query(..., {
async protector({ input, output: doc, subject }) {
if (doc && await Kilpi.isAuthorized("docs:read", doc)) {
return doc;
}
return null;
}
})
```
----------------------------------------
TITLE: Perform an authorization check with Kilpi
DESCRIPTION: Demonstrates how to use Kilpi's `authorize` method to check if a user is permitted to perform a specific action on a resource. This is a core authorization check for immediate access control.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/README.md#_snippet_0
LANGUAGE: ts
CODE:
```
await Kilpi.authorize("documents:update", document);
```
----------------------------------------
TITLE: Compare Policy Authorization with and Without Subject Pattern
DESCRIPTION: This snippet compares two approaches to policy authorization: one without the subject pattern, requiring manual user fetching and passing, and another with the subject pattern, where Kilpi automatically provides the subject and narrows its type, reducing boilerplate.
SOURCE: https://github.com/jussinevavuori/kilpi/blob/main/apps/docs/docs/concepts/subject.mdx#_snippet_5
LANGUAGE: ts
CODE:
```
// Without subject pattern
const user = await getUserFromMyAuthProvider();
await Kilpi.authorize(user, "docs:read", doc);
if (!user) {
throw new Error("User not found");
}
console.log(user.name);
// With subject pattern (and narrowed down `user` type)
const user = await Kilpi.authorize("docs:read", doc);
console.log(user.name);
```