VVibeFlui

Permission Resolvers

Permission resolvers decide whether an action is allowed in the UI.

Permission checks in VibeFlui improve UI behavior, but the backend must still enforce authorization.

Schema-level permission

schemas/users/user-actions.schema.ts

TS
export const usersSchema = {
  version: "1.0.0",
  resource: "users",
  permissions: {
    create: "users.canCreate",
    update: "users.canUpdate",
    delete: "users.canDelete"
  },
  actions: {
    delete: {
      enabled: true,
      label: "Delete",
      icon: "lucide:trash-2",
      variant: "danger",
      handler: "users.delete"
    }
  },
  registry: {
    permissionResolvers: ["users.canCreate", "users.canUpdate", "users.canDelete"]
  }
} as const;

Action-level permission

schemas/users/user-actions.ts

TS
export const deleteAction = {
  key: "delete",
  label: "Delete",
  permission: "users.canDelete"
} as const;

Runtime registry

lib/vibeflui/registry.ts

TS
import { createRegistry } from "@vibeflui/core";

export const registry = createRegistry({
  permissionResolvers: {
    "users.canDelete": async ({ row, permissionContext }) => {
      const hasPermission = permissionContext?.permissions?.includes("users:delete");
      const isOwner = row?.role === "owner";

      return Boolean(hasPermission && !isOwner);
    }
  }
});

Permission context

Pass roles, permissions, user data, or custom values through FluiKitProvider.

app/users/page.tsx

TSX
<FluiKitProvider
  registry={registry}
  permissionContext={{
    roles: ["admin"],
    permissions: ["users:create", "users:update", "users:delete"],
    user: { id: "user_1" }
  }}
>
  <FluiKit schema={usersSchema} />
</FluiKitProvider>

String and array permissions

If a permission string has no resolver, runtime checks permissionContext.roles and permissionContext.permissions.

schemas/users/user-actions.schema.ts

TS
export const usersSchema = {
  version: "1.0.0",
  resource: "users",
  actions: {
    create: {
      label: "Create user",
      permission: "users:create"
    },
    delete: {
      label: "Delete",
      permission: ["admin", "owner"]
    }
  }
} as const;

An array passes when at least one role or permission is present.

Condition permissions

Permissions can also use conditions.

schemas/users/user-actions.schema.ts

TS
export const usersSchema = {
  version: "1.0.0",
  resource: "users",
  permissions: {
    approve: {
      key: "status",
      operator: "equals",
      value: "pending"
    }
  },
  actions: {
    row: [
      {
        key: "approve",
        label: "Approve",
        handler: "users.approve"
      }
    ]
  }
} as const;

Condition objects are supported in root permissions. Action-level permission accepts a string or string array.

Backend responsibility

Never rely on UI permission checks alone. A hidden or disabled button is not authorization. The backend must reject unauthorized requests.