@kilpi/core
updates
Policies and deny()
and grant()
deny()
and grant()
have been renamed to Deny()
and Grant()
.
import { deny, grant } from "@kilpi/core"; import { Deny, Grant } from "@kilpi/core";
export const Kilpi = createKilpi({ policies: { async isAdmin(subject) { if (subject.role === "admin") return grant(subject); if (subject.role === "admin") return Grant(subject); return deny({ message: "User is not an admin" }); return Deny({ message: "User is not an admin" }); } }})
Remove scope
Remove all references to scope. It is no longer a part of Kilpi with the simplifications made in v1.
These include Kilpi.runInScope
, Kilpi.scoped
and the Kilpi.hooks.onRequestScope
hook.
return await Kilpi.runInScope(async () => { return await handler();}, req)
export const POST = Kilpi.scoped(async (req: Request) => { return await handler();})
await handler();
export const POST = async (req: Request) => { return await handler();}
Instead of passing ctx
via scope, you now pass it to the authorize()
method directly.
await Kilpi.runInScope(async () => { await Kilpi.authorize("posts:edit", post);}, ctx);
await Kilpi.posts.edit(post).authorize({ ctx });
Kilpi.onUnauthorized
Remove all Kilpi.onUnauthorized
calls.
Fall back to using the default onUnauthorizedAssert
provided to Kilpi
, the Kilpi.$hooks.onUnauthorizedAssert
hooks or the .assert(() => { ... })
callback feature (or your own solution built on these).
createKilpi
settings.disableSubjectCaching
has been removed and settings.defaultOnUnauthorized
has been renamed to onUnauthorizedAssert
.
export const Kilpi = createKilpi({ // ... settings: { async defaultOnUnauthorized(decision) { await redirect(`/sign-in?message=${decision.message}`); }, disableSubjectCaching: true, }, async onUnauthorizedAssert(decision) { await redirect(`/sign-in?message=${decision.message}`); },})
Kilpi.isAuthorized
, Kilpi.authorize
and Kilpi.getAuthorizationDecision
Update to the new .authorize()
API.
const granted = await Kilpi.isAuthorized("posts:edit", post);const { granted } = await Kilpi.posts.edit(post).authorize();
const decision = await Kilpi.getAuthorizationDecision("posts:create");const decision = await Kilpi.posts.create().authorize();
const subject = await Kilpi.authorize("posts:delete", post);const { subject } = await Kilpi.posts.delete(post).authorize().assert();
Kilpi.getSubject
The Kilpi.getSubject
method has been renamed to Kilpi.$getSubject
.
const subject = await Kilpi.getSubject();const subject = await Kilpi.$getSubject();
Kilpi.unauthorized()
The unauthorized()
method has been removed, but you can mimic its behavior with a never
policy.
export const Kilpi = createKilpi({ // ... policies: { never: () => Deny(), // ... }})
await Kilpi.unauthorized();await Kilpi.never().authorize().assert();
Kilpi.query
Kilpi.query
is renamed to Kilpi.$query
and the API has been updated.
const getPost = await Kilpi.query(const getPost = await Kilpi.$query( async (id) => { ... }, { async protector({ subject, input, output }) { ... }, async authorize({ subject, input, output }) { ... }, })
const post = await getPost.protect("1");const post = await getPost.authorized("1");
const post = await getPost.unsafe("1");const post = await getPost.unauthorized("1");
Kilpi.hooks
Kilpi.hooks
is renamed to Kilpi.$hooks
and the Kilpi.onRequestScope
hook is removed.
EndpointPlugin
The EndpointPlugin
’s Kilpi.createPostEndpoint
method is renamed to Kilpi.$createPostEndpoint
.
AuditPlugin
The AuditPlugin
’s interface under Kilpi.audit
has been renamed to Kilpi.$audit
.
@kilpi/client
updates
KilpiClient.fetchIsAuthorized
The old fetchIsAuthorized
API is now more similar to the server-side API. It also returns the decision and not only a boolean.
const granted = await KilpiClient.fetchIsAuthorized({ action: "posts.edit", object: post, queryOptions: { signal },});const { granted } = await KilpiClient.posts.edit(post).authorize({ signal });
KilpiClient.fetchSubject
The fetchSubject
method has been removed. You can mimic the old behavior with an always
policy if needed.
export const Kilpi = createKilpi({ // ... policies: { always: (subject) => Grant(subject), // ... }})
const subject = await KilpiClient.fetchSubject();const decision = await KilpiClient.always().authorize();const subject = decision.granted ? decision.subject : null; // Should always be granted
KilpiClient.clearCache
Client-side cache invalidation has been improved with the new Kilpi.$cache
API.
await KilpiClient.clearCache();await KilpiClient.$cache.invalidate();
@kilpi/react-server
updates
ReactServerComponentPlugin
The ReactServerComponentPlugin
has been renamed to ReactServerPlugin
and the API has been updated.
import { ReactServerComponentPlugin } from "@kilpi/react-server";import { ReactServerPlugin } from "@kilpi/react-server";
export const Kilpi = createKilpi({ // ... plugins: [ReactServerComponentPlugin()], plugins: [ReactServerPlugin()],})
<Access />
component
The <Access />
component has been redesigned as the <Authorize />
component.
const { Access } = Kilpi.ReactServer.createComponents();const { Authorize } = Kilpi.$createReactServerComponents();
<Access to="posts:delete" on={post} Loading={<LoadingUI />} Unauthorized={<UnauthorizedUI />}> <DeletePostForm post={post} /></Access>
<Authorize policy={Kilpi.posts.delete(post)} Pending={<LoadingUI />} Unauthorized={<UnauthorizedUI />}> <DeletePostForm post={post} /></Authorize>
Kilpi.onUnauthorized
Replace Kilpi.onUnauthorized
calls with Kilpi.$onUnauthorizedRscAssert
calls.
Kilpi.onUnauthorized(async (decision) => { // ...})Kilpi.$onUnauthorizedRscAssert(async (decision) => { // ...})
@kilpi/react-client
updates
ReactClientComponentPlugin
The ReactClientComponentPlugin
has been renamed to ReactClientPlugin
and the API has been updated.
import { ReactClientComponentPlugin } from "@kilpi/react-client";import { ReactClientPlugin } from "@kilpi/react-client";
export const KilpiClient = createKilpiClient({ // ... plugins: [ReactClientComponentPlugin()], plugins: [ReactClientPlugin()],})
<ClientAccess />
component
const { ClientAccess } = KilpiClient.createComponents();const { AuthorizeClient } = KilpiClient.$createReactClientComponents();
<ClientAccess to="posts:delete" on={post} Loading={<LoadingUI />} Unauthorized={<UnauthorizedUI />} Error={<ErrorUI />}> <DeletePostForm post={post} /></ClientAccess>
<AuthorizeClient policy={KilpiClient.posts.delete(post)} Pending={<LoadingUI />} Unauthorized={<UnauthorizedUI />} Error={<ErrorUI />}> <DeletePostForm post={post} /></AuthorizeClient>
useIsAuthorized()
hook
The useIsAuthorized
hook has been renamed to useAuthorize()
and is available on each policy separately.
const { useIsAuthorized } = KilpiClient.ReactClient.createComponents();
const { isAuthorized, status, error, // ...} = useIsAuthorized("comments:edit", comment);
const { granted, status, error, // ...} = KilpiClient.comments.edit(comment).useAuthorize();
useSubject()
hook
The useSubject
hook has been removed. You can mimic the old behavior with an always
policy if needed.
const { useSubject } = KilpiClient.ReactClient.createComponents();
const { subject, status, error, // ...} = useSubject();
export const KilpiClient = createKilpiClient({ // ... policies: { always: (subject) => Grant(subject), // ... }})
function useSubject() { const query = KilpiClient.always().useAuthorize(); return { ...query, subject: query.granted ? query.decision.subject : null };}
const { subject, status, error, // ...} = useSubject();