### Install Dependencies and Run Locally Source: https://github.com/dbjpanda/convex-authz/blob/main/CONTRIBUTING.md Installs project dependencies and starts the local development server. ```sh npm i npm run dev ``` -------------------------------- ### CRM Setup Example Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Demonstrates setting up a hierarchy of relationships for a CRM scenario. ```typescript // Setup CRM hierarchy using the Authz client const setupCRM = async (ctx) => { // Sales rep Alice is on sales team await authz.addRelation(ctx, { type: "user", id: "alice" }, "member", { type: "team", id: "sales" }); // Sales team owns Acme Corp account await authz.addRelation(ctx, { type: "team", id: "sales" }, "owner", { type: "account", id: "acme_corp" }); // Acme Corp has a big deal await authz.addRelation(ctx, { type: "account", id: "acme_corp" }, "parent", { type: "deal", id: "big_deal" }); // Now Alice can access the deal through the relationship chain! }; ``` -------------------------------- ### Install Dependencies Source: https://github.com/dbjpanda/convex-authz/blob/main/example/README.md Run this command from the root of the project to install all necessary dependencies. ```bash npm install ``` -------------------------------- ### Install the package Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Use npm to add the authorization component to your project. ```bash npm install @djpanda/convex-authz ``` -------------------------------- ### Start Development Server Source: https://github.com/dbjpanda/convex-authz/blob/main/example/README.md This command starts the local development server. After running, open http://localhost:5173 in your browser. ```bash npm run dev ``` -------------------------------- ### Install One-Off Package Source: https://github.com/dbjpanda/convex-authz/blob/main/PUBLISHING.md Installs a package from a local .tgz file. Useful for testing before publishing. ```sh npm install ./path/to/your-package.tgz ``` -------------------------------- ### Development workflow commands Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Standard commands for installing dependencies, running development mode, testing, and building. ```bash # Install dependencies npm install # Run development mode npm run dev # Run tests npm test # Build for production npm run build # Type check npm run typecheck ``` -------------------------------- ### Install Alpha Version Source: https://github.com/dbjpanda/convex-authz/blob/main/PUBLISHING.md Installs the alpha version of the package from npm. ```sh npm install @your-package@alpha ``` -------------------------------- ### Run Testing Suite Source: https://github.com/dbjpanda/convex-authz/blob/main/CONTRIBUTING.md Installs dependencies, cleans previous builds, and runs type checking, linting, and tests. ```sh npm ci npm run clean npm run typecheck npm run lint npm run test ``` -------------------------------- ### Build One-Off Package Source: https://github.com/dbjpanda/convex-authz/blob/main/PUBLISHING.md Cleans the distribution directory, builds the package, and creates a .tgz file for manual installation. ```sh npm run clean ``` ```sh npm run build ``` ```sh npm pack ``` -------------------------------- ### Install and find AI coding agent skills Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Commands to add the library as an agent skill or search for related skills. ```bash npx skills add dbjpanda/convex-authz ``` ```bash npx skills find convex ``` -------------------------------- ### Development Commands Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Common npm commands for managing the project, including installation, development server, testing, building, and type checking. ```bash # Install dependencies npm install # Run development mode npm run dev # Run tests npm test # Build for production npm run build # Type check npm run typecheck ``` -------------------------------- ### Role Composition Example Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Combine multiple roles using 'includes' to create new roles with a union of permissions from the included roles, plus any directly assigned permissions. ```typescript const roles = defineRoles(permissions, { editor: { documents: ["create", "read", "update"] }, billing_admin: { billing: ["view", "manage"] }, billing_manager: { includes: ["editor", "billing_admin"], settings: ["view"] }, }); ``` -------------------------------- ### Zanzibar Model Diagram Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Illustrates the Google Zanzibar model including relation tuples, authorization model, and a check example. This visual aid helps understand the data flow and logic for authorization checks. ```text ┌─────────────────────────────────────────────────────────────────────────────┐ │ Google Zanzibar Model │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ Relation Tuples (stored): │ ┌────────────────────────────────────────────────────────────────────┐ │ │ │ (user:alice, member, team:sales) │ │ (team:sales, owner, account:acme) │ │ (account:acme, parent, deal:big_deal) │ └────────────────────────────────────────────────────────────────────┘ │ │ │ │ Authorization Model (defines inheritance): │ ┌────────────────────────────────────────────────────────────────────┐ │ │ │ type deal │ │ │ │ relations │ │ │ │ define parent: [account] │ │ define viewer: viewer from parent ← Computed relation │ │ │ └────────────────────────────────────────────────────────────────────┘ │ │ │ │ Check: "Can alice view deal:big_deal?" │ ┌────────────────────────────────────────────────────────────────────┐ │ │ │ 1. deal:big_deal.viewer = viewer from parent │ │ │ │ 2. parent = account:acme │ │ │ │ 3. account:acme.viewer = member from owner │ │ │ │ 4. owner = team:sales │ │ │ │ 5. team:sales.member includes user:alice ✓ │ │ │ │ → ALLOWED │ │ │ └────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` -------------------------------- ### Role Inheritance Example Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Define roles that inherit permissions from parent roles to simplify role management and avoid repetition. Ensure 'inherits' and 'includes' are not used as permission resource names. ```typescript const roles = defineRoles(permissions, { viewer: { documents: ["read"] }, editor: { inherits: "viewer", documents: ["create", "update"] }, admin: { inherits: "editor", documents: ["delete"], settings: ["manage"] }, }); ``` -------------------------------- ### Get All User Attributes Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Retrieves all attributes associated with a given user. Returns an array of key-value pairs. ```typescript const attributes = await authz.getUserAttributes(ctx, userId); // Returns: [{ key: "department", value: "engineering" }, { key: "clearanceLevel", value: 5 }] ``` -------------------------------- ### Run Development and Build Commands Source: https://github.com/dbjpanda/convex-authz/blob/main/CLAUDE.md Common CLI commands for testing, building, and running the development environment. ```bash npm test # Run vitest (run mode + type checking) npm run test:watch # Vitest in watch mode npm run build # Compile via tsconfig.build.json → dist/ npm run build:clean # Remove dist + tsbuildinfo, full codegen rebuild npm run build:codegen # Generate Convex component code + rebuild npm run lint # ESLint on all files npm run typecheck # tsc --noEmit across src, example, and example/convex npm run dev # Parallel: convex dev + vite (example app) + build watcher ``` ```bash npx vitest run src/component/queries.test.ts ``` ```bash npx vitest run -t "test name pattern" ``` ```bash npm run test:debug ``` -------------------------------- ### Initialize Authz Client Source: https://context7.com/dbjpanda/convex-authz/llms.txt Define permissions and roles, then initialize the Authz client instance. ```typescript // convex/authz.ts import { Authz, definePermissions, defineRoles } from "@djpanda/convex-authz"; import { components } from "./_generated/api"; const permissions = definePermissions({ documents: { create: true, read: true, update: true, delete: true }, settings: { view: true, manage: true }, billing: { view: true, manage: true }, }); const roles = defineRoles(permissions, { admin: { documents: ["create", "read", "update", "delete"], settings: ["view", "manage"], billing: ["view", "manage"], }, editor: { documents: ["create", "read", "update"], settings: ["view"], }, viewer: { documents: ["read"], }, }); export const authz = new Authz(components.authz, { permissions, roles, tenantId: "my-app", }); ``` -------------------------------- ### GET getAuditLog Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Retrieves authorization audit logs for compliance and debugging, supporting both simple array returns and cursor-based pagination. ```APIDOC ## GET getAuditLog ### Description Retrieves a list of audit logs. Can be filtered by user or action type. Supports simple limit-based retrieval or cursor-based pagination for large datasets. ### Parameters #### Query Parameters - **userId** (string) - Optional - Filter logs by the affected user. - **action** (string) - Optional - Filter logs by the action type (e.g., role_assigned, permission_granted). - **limit** (number) - Optional - Maximum number of items to return (default 100). - **numItems** (number) - Optional - Number of items for cursor-based pagination. - **cursor** (string) - Optional - Cursor for the next page of results. ### Response #### Success Response (200) - **Array/Object** - Returns an array of log entries or an object containing { page, isDone, continueCursor } when using pagination. ### Response Example { "_id": "...", "timestamp": 1704672000000, "actorId": "admin_user", "action": "role_assigned", "userId": "target_user", "details": { "role": "editor", "scope": { "type": "team", "id": "team_123" } } } ``` -------------------------------- ### Seed Demo Data Source: https://github.com/dbjpanda/convex-authz/blob/main/example/README.md Execute this command to populate the database with demo data, including organizations, users, documents, and role assignments. ```bash npx convex run seed:seedAll ``` -------------------------------- ### Deploy Alpha Version Source: https://github.com/dbjpanda/convex-authz/blob/main/PUBLISHING.md Runs the 'alpha' script to create a prerelease version with an '@alpha' tag, publishes it to npm, and pushes the code and tag. ```sh npm run alpha ``` -------------------------------- ### Assigning Roles with Expiration Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Roles can be assigned with an expiration time. The expiration is specified in milliseconds since the Unix epoch. This example sets the role to expire in 24 hours. ```typescript // With expiration (24 hours) await authz.assignRole(ctx, userId, "admin", undefined, Date.now() + 86400000); ``` -------------------------------- ### Deploy Release Version Source: https://github.com/dbjpanda/convex-authz/blob/main/PUBLISHING.md Runs the 'release' script to create a patch version, publishes it as 'latest' to npm, and pushes the code and tag. ```sh npm run release ``` -------------------------------- ### Run Package Tests Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Command to execute the test suite for the convex-authz package. Ensure you are in the 'packages/authz' directory before running. ```bash cd packages/authz npm test ``` -------------------------------- ### Define Permissions and Roles Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Define granular permissions and then group them into roles. Finally, create an Authz client instance with your defined permissions and roles. ```typescript // convex/authz.ts import { Authz, definePermissions, defineRoles, } from "@djpanda/convex-authz"; import { components } from "./_generated/api"; // Step 1: Define permissions const permissions = definePermissions({ documents: { create: true, read: true, update: true, delete: true, }, settings: { view: true, manage: true, }, }); // Step 2: Define roles const roles = defineRoles(permissions, { admin: { documents: ["create", "read", "update", "delete"], settings: ["view", "manage"], }, editor: { documents: ["create", "read", "update"], settings: ["view"], }, viewer: { documents: ["read"], }, }); // Step 3: Create the authz client export const authz = new Authz(components.authz, { permissions, roles, tenantId: "my-app", }); ``` -------------------------------- ### Use React Hooks and PermissionGate Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Demonstrates usage of authorization hooks and the PermissionGate component for UI-level access control. ```tsx import { useCanUser, useUserRoles, useRequirePermission, PermissionGate, } from "@djpanda/convex-authz/react"; function DocumentList() { const { allowed, isLoading } = useCanUser("documents:read"); if (isLoading) return ; if (!allowed) return

You cannot view documents.

; return
{/* list */}
; } function AdminPanel() { useRequirePermission("settings:manage"); // throws if denied; wrap in error boundary return
Admin content
; } function EditButton({ docId }: { docId: string }) { return ( No access} loadingFallback={Checking…} > ); } ``` -------------------------------- ### Wrap App with AuthzProvider Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Configure the React provider with references to your Convex authorization queries. ```tsx import { AuthzProvider } from "@djpanda/convex-authz/react"; import { api } from "./convex/_generated/api"; ``` -------------------------------- ### Organize Permissions by Domain Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Split permission and role definitions into separate files for better maintainability in larger applications. ```typescript // convex/permissions/documents.ts export const documentPermissions = { documents: { create: true, read: true, update: true, delete: true }, }; export const documentRoles = { editor: { documents: ["create", "read", "update"] as const }, viewer: { documents: ["read"] as const }, }; // convex/permissions/billing.ts export const billingPermissions = { billing: { view: true, manage: true }, }; export const billingRoles = { billing_admin: { billing: ["view", "manage"] as const }, }; ``` ```typescript // convex/authz.ts — merge all domains import { Authz, definePermissions, defineRoles } from "@djpanda/convex-authz"; import { components } from "./_generated/api"; import { documentPermissions, documentRoles } from "./permissions/documents"; import { billingPermissions, billingRoles } from "./permissions/billing"; const permissions = definePermissions(documentPermissions, billingPermissions); const roles = defineRoles(permissions, documentRoles, billingRoles); export const authz = new Authz(components.authz, { permissions, roles, tenantId: "my-app" }); ``` -------------------------------- ### Organize Permissions by Domain Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Split permission and role definitions into separate files by domain for better organization in larger applications. This approach keeps related definitions self-contained. ```typescript // convex/permissions/documents.ts export const documentPermissions = { documents: { create: true, read: true, update: true, delete: true }, }; export const documentRoles = { editor: { documents: ["create", "read", "update"] as const }, viewer: { documents: ["read"] as const }, }; // convex/permissions/billing.ts export const billingPermissions = { billing: { view: true, manage: true }, }; export const billingRoles = { billing_admin: { billing: ["view", "manage"] as const }, }; ``` -------------------------------- ### Publish Package to npm Source: https://github.com/dbjpanda/convex-authz/blob/main/PUBLISHING.md Publishes the package to npm with public access. Ensure you have logged in and built the package first. ```sh npm publish --access public ``` -------------------------------- ### React Hooks and PermissionGate Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Demonstrates the usage of React hooks for checking user permissions and roles, and the PermissionGate component for conditional rendering. ```APIDOC ## React Hooks and PermissionGate ### Description Provides hooks and a component for managing access control within React applications using Convex. ### Hooks - **useCanUser(permission, options?)**: Returns `{ allowed, isLoading, error }`. Options: `{ userId?, scope? }`. - **useUserRoles(options?)**: Returns `{ roles, isLoading, error }`. Options: `{ userId?, scope? }`. - **useRequirePermission(permission, options?)**: Throws an error if the user does not have the required permission. Use with an error boundary. ### Component - **PermissionGate**: Renders `children` when the user has the specified permission, `fallback` when denied, and `loadingFallback` (optional) while checking. ### Usage Examples ```tsx import { useCanUser, useUserRoles, useRequirePermission, PermissionGate, } from "@djpanda/convex-authz/react"; function DocumentList() { const { allowed, isLoading } = useCanUser("documents:read"); if (isLoading) return ; if (!allowed) return

You cannot view documents.

; return
{/* list */}
; } function AdminPanel() { useRequirePermission("settings:manage"); // throws if denied; wrap in error boundary return
Admin content
; } function EditButton({ docId }: { docId: string }) { return ( No access} loadingFallback={Checking…} > ); } ``` ### Automatic UI Updates Convex’s reactivity ensures that UI components automatically update when permissions or roles change on the backend. ``` -------------------------------- ### Test Authorization with convex-test Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Demonstrates how to use `convex-test` to simulate mutations and queries for testing authorization logic, including assigning and checking user roles. ```typescript import { convexTest } from "convex-test"; import { describe, expect, it } from "vitest"; import schema from "./component/schema.js"; import { api } from "./component/_generated/api.js"; describe("authorization", () => { it("should assign and check roles", async () => { const t = convexTest(schema, modules); await t.mutation(api.mutations.assignRole, { userId: "user_123", role: "admin", }); const hasRole = await t.query(api.queries.hasRole, { userId: "user_123", role: "admin", }); expect(hasRole).toBe(true); }); }); ``` -------------------------------- ### Clear All Demo Data Command Source: https://github.com/dbjpanda/convex-authz/blob/main/example/README.md A command to clear all demo data from the application. ```bash npx convex run seed:clearAll ``` -------------------------------- ### Upgrade from IndexedAuthz to Authz Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Migrate from `IndexedAuthz` to `Authz` in v2. Replace the import and use the same constructor. ```typescript // Before (v1) — this import no longer works in v2 // import { IndexedAuthz } from "@djpanda/convex-authz"; // const authz = new IndexedAuthz(components.authz, { permissions, roles, tenantId: "my-app" }); // After (v2) — same constructor, just rename the class import { Authz } from "@djpanda/convex-authz"; const authz = new Authz(components.authz, { permissions, roles, tenantId: "my-app" }); ``` -------------------------------- ### Initialize and Execute Convex Tests Source: https://github.com/dbjpanda/convex-authz/blob/main/CLAUDE.md Uses convex-test to initialize a fresh database environment and execute mutations and queries. ```typescript import { convexTest } from "convex-test"; import schema from "./schema.js"; import { api } from "./_generated/api.js"; const t = convexTest(schema, import.meta.glob("./**/*.ts")); await t.mutation(api.mutations.assignRole, { userId, role, ... }); const result = await t.query(api.queries.hasRole, { userId, role, ... }); ``` -------------------------------- ### AuthzProvider (React) Source: https://context7.com/dbjpanda/convex-authz/llms.txt React context provider that enables permission-aware UI components with real-time updates via Convex reactivity. ```APIDOC ## AuthzProvider (React) ### Description React context provider that enables permission-aware UI components with real-time updates via Convex reactivity. ### Method N/A (This is a React component) ### Endpoint N/A ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```tsx // App.tsx import { AuthzProvider } from "@djpanda/convex-authz/react"; import { api } from "./convex/_generated/api"; import { useAuth } from "./auth"; // Your auth hook function App() { const { userId } = useAuth(); return ( ); } // convex/app.ts - Required Convex queries export const checkPermissionScoped = query({ args: { userId: v.string(), permission: v.string(), scope: v.optional(v.object({ type: v.string(), id: v.string() })), }, handler: async (ctx, args) => { return await authz.can(ctx, args.userId, args.permission as any, args.scope); }, }); export const getRoles = query({ args: { userId: v.string(), scope: v.optional(v.object({ type: v.string(), id: v.string() })), }, handler: async (ctx, args) => { return await authz.getUserRoles(ctx, args.userId, args.scope); }, }); ``` ### Response #### Success Response (200) This component does not directly return a response. It provides context to child components. #### Response Example N/A ``` -------------------------------- ### Frontend Hooks and Components Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md These hooks and components from '@djpanda/convex-authz/react' allow for dynamic UI updates based on user permissions and roles. They integrate seamlessly with Convex's reactivity. ```APIDOC ## Frontend Hooks and Components ### `useCanUser(permission, options?)` - **Description**: Returns `{ allowed, isLoading, error }` to check if the current user has a specific permission. - **Options**: `{ userId?, scope? }`. `userId` defaults to `defaultUserId` from the provider. ### `useUserRoles(options?)` - **Description**: Returns `{ roles, isLoading, error }` to get the roles of the current user. - **Options**: `{ userId?, scope? }`. `userId` defaults to `defaultUserId` from the provider. ### `useRequirePermission(permission, options?)` - **Description**: Throws an error if the user does not have the specified permission. Use with an error boundary. - **Options**: `{ userId?, scope? }`. `userId` defaults to `defaultUserId` from the provider. ### `PermissionGate` Component - **Description**: Renders `children` if the user has the required permission, `fallback` if denied, and `loadingFallback` (optional) while checking. - **Props**: - `permission` (string) - The permission to check. - `scope` (object, optional) - The scope for the permission check. - `fallback` (ReactNode) - Content to render when access is denied. - `loadingFallback` (ReactNode, optional) - Content to render while loading. ### Request Example (React Component) ```tsx import { useCanUser, useUserRoles, useRequirePermission, PermissionGate, } from "@djpanda/convex-authz/react"; function DocumentList() { const { allowed, isLoading } = useCanUser("documents:read"); if (isLoading) return ; if (!allowed) return

You cannot view documents.

; return
{/* list */}
; } function AdminPanel() { useRequirePermission("settings:manage"); // throws if denied; wrap in error boundary return
Admin content
; } function EditButton({ docId }: { docId: string }) { return ( No access} loadingFallback={Checking…} > ); } ``` ``` -------------------------------- ### Define Roles with Inheritance Source: https://context7.com/dbjpanda/convex-authz/llms.txt Configure roles using inheritance and composition to manage complex permission structures. ```typescript import { definePermissions, defineRoles } from "@djpanda/convex-authz"; const permissions = definePermissions({ documents: { create: true, read: true, update: true, delete: true }, settings: { view: true, manage: true }, billing: { view: true, manage: true }, }); // Role inheritance: admin > editor > viewer const roles = defineRoles(permissions, { viewer: { documents: ["read"] }, editor: { inherits: "viewer", documents: ["create", "update"] }, admin: { inherits: "editor", documents: ["delete"], settings: ["view", "manage"], billing: ["view", "manage"], }, // Role composition with includes billing_manager: { includes: ["editor"], billing: ["view", "manage"] }, }); ``` -------------------------------- ### Configure Audit Log Retention Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Set environment variables to configure daily audit retention jobs. Specify `AUDIT_RETENTION_DAYS` to delete entries older than a certain number of days, or `AUDIT_RETENTION_MAX_ENTRIES` to cap the total number of entries by deleting the oldest ones. At least one variable must be set to enable retention. ```bash AUDIT_RETENTION_DAYS=90 AUDIT_RETENTION_MAX_ENTRIES=100000 ``` -------------------------------- ### Manually Publish Release Version Source: https://github.com/dbjpanda/convex-authz/blob/main/PUBLISHING.md Manually creates a new minor or major version, publishes it to npm, and pushes the tags. ```sh npm version minor # or major ``` ```sh npm publish ``` ```sh git push --follow-tags ``` -------------------------------- ### Authz.withTenant Source: https://context7.com/dbjpanda/convex-authz/llms.txt Creates a new Authz instance scoped to a different tenant for cross-tenant administrative operations. ```APIDOC ## Authz.withTenant ### Description Creates a new Authz instance scoped to a different tenant for cross-tenant administrative operations. ### Method N/A (This is a method on an existing Authz instance) ### Endpoint N/A ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```typescript import { mutation } from "./_generated/server"; import { v } from "convex/values"; import { authz } from "./authz"; export const crossTenantAdminOperation = mutation({ args: { targetTenantId: v.string(), userId: v.string(), role: v.string(), }, handler: async (ctx, args) => { // Get authz instance for different tenant const targetAuthz = authz.withTenant(args.targetTenantId); // Check roles in target tenant const roles = await targetAuthz.getUserRoles(ctx, args.userId); // Assign role in target tenant await targetAuthz.assignRole( ctx, args.userId, args.role as "admin" | "editor" | "viewer" ); return { previousRoles: roles }; }, }); ``` ### Response #### Success Response (200) - **previousRoles** (object) - The roles the user previously had in the target tenant. #### Response Example ```json { "previousRoles": { "roles": ["viewer"] } } ``` ``` -------------------------------- ### Registering Authz and Tenants Components Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Register both the authz and tenants components in your `convex.config.ts` file. This makes their functionalities available to your application. ```typescript // convex/convex.config.ts — register both components import { defineApp } from "convex/server"; import authz from "@djpanda/convex-authz/convex.config"; import tenants from "@djpanda/convex-tenants/convex.config"; const app = defineApp(); app.use(authz); app.use(tenants); export default app; ``` -------------------------------- ### Permission Checks Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Methods for verifying user permissions and requirements. ```APIDOC ## Permission Checks ### Description Methods to check if a user has specific permissions or to enforce requirements. ### Methods - **can(ctx, userId, permission, scope?)**: Returns a boolean indicating if the user has the permission. - **canAny(ctx, userId, permissions[], scope?)**: Returns a boolean if the user has any of the provided permissions (max 100). - **require(ctx, userId, permission, scope?)**: Throws an error if the user does not have the required permission. ``` -------------------------------- ### Define Permissions Source: https://context7.com/dbjpanda/convex-authz/llms.txt Create type-safe permission maps or merge multiple permission sets for modularity. ```typescript import { definePermissions } from "@djpanda/convex-authz"; // Single definition const permissions = definePermissions({ documents: { create: true, read: true, update: true, delete: true }, settings: { view: true, manage: true }, users: { invite: true, remove: true, manage: true }, }); // Merging multiple permission sets (e.g., with component defaults) import { TENANTS_PERMISSIONS } from "@djpanda/convex-tenants"; const mergedPermissions = definePermissions( TENANTS_PERMISSIONS, { billing: { view: true, manage: true }, reports: { generate: true, export: true }, } ); ``` -------------------------------- ### Database Table Indexes Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Optimized indexes for common query patterns across authorization tables. ```typescript // roleAssignments .index("by_user", ["userId"]) .index("by_role", ["role"]) .index("by_user_and_role", ["userId", "role"]) // effectivePermissions (O(1) lookup) .index("by_user_permission_scope", ["userId", "permission", "scopeKey"]) // relationships .index("by_subject_relation_object", ["subjectType", "subjectId", "relation", "objectType", "objectId"]) ``` -------------------------------- ### Configure Authz Component for Single-Tenant Apps Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Configure the Authz component for single-tenant applications by providing a constant string for the `tenantId`. ```typescript // Single-tenant apps — pass any constant string const authz = new Authz(components.authz, { permissions, roles, tenantId: "my-app", }); ``` -------------------------------- ### Grant and Deny Permissions with Patterns Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Use wildcard patterns to manage permissions for entire resource families or action types. ```typescript // Grant all document actions await authz.grantPermission(ctx, userId, "documents:*", undefined, "Full document access"); // Deny read on any resource await authz.denyPermission(ctx, userId, "*:read", undefined, "Read access revoked"); ``` -------------------------------- ### Configure Authz Component for Multi-Tenant Apps Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Configure the Authz component for multi-tenant applications by passing the current organization or tenant ID to the `tenantId` parameter. ```typescript // Multi-tenant apps — pass the current organization/tenant ID const authz = new Authz(components.authz, { permissions, roles, tenantId: currentOrgId, }); ``` -------------------------------- ### User Offboarding Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Methods for cleaning up user data and permissions upon offboarding. ```APIDOC ## User Offboarding ### Description Methods to handle user deprovisioning and offboarding, including full wipes of roles and attributes. ### Methods - **offboardUser(ctx, userId, options?)**: Offboards a user with configurable options. - **deprovisionUser(ctx, userId, options?)**: Performs a full wipe of roles, overrides, attributes, and relationships. ``` -------------------------------- ### Authz Component Configuration Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Configure the Authz component for single-tenant or multi-tenant applications by providing the appropriate `tenantId`. ```typescript // Single-tenant apps — pass any constant string const authz = new Authz(components.authz, { permissions, roles, tenantId: "my-app", }); ``` ```typescript // Multi-tenant apps — pass the current organization/tenant ID const authz = new Authz(components.authz, { permissions, roles, tenantId: currentOrgId, }); ``` -------------------------------- ### Define ABAC Policies with Convex Authz Source: https://context7.com/dbjpanda/convex-authz/llms.txt Defines static and deferred ABAC policies with conditions evaluated at permission check time. Requires importing `Authz`, `definePermissions`, `defineRoles`, and `definePolicies`. ```typescript import { Authz, definePermissions, defineRoles, definePolicies } from "@djpanda/convex-authz"; import { components } from "./_generated/api"; const permissions = definePermissions({ documents: { read: true, update: true, delete: true }, reports: { view: true, export: true }, }); const roles = defineRoles(permissions, { user: { documents: ["read"] }, analyst: { reports: ["view", "export"] }, }); // Define ABAC policies const policies = definePolicies({ "documents:read": { type: "static", condition: (ctx) => ctx.getAttribute("verified") === true, message: "Only verified users can read documents", }, "documents:update": { type: "static", condition: (ctx) => ctx.hasRole("editor") || ctx.resource?.ownerId === ctx.subject.userId, message: "Only editors or document owners can update", }, "reports:export": { type: "deferred", condition: (ctx) => { const hour = new Date().getUTCHours(); return hour >= 9 && hour <= 17; // Business hours only }, message: "Report exports only during business hours (9am-5pm UTC)", }, }); const authz = new Authz(components.authz, { permissions, roles, policies, tenantId: "my-app" }); // Using canWithContext for runtime context evaluation export const exportReport = mutation({ args: { userId: v.string(), reportId: v.string(), ipAddress: v.string() }, handler: async (ctx, args) => { // Pass runtime context for deferred policy evaluation const allowed = await authz.canWithContext( ctx, args.userId, "reports:export", { type: "report", id: args.reportId }, { ipAddress: args.ipAddress, timestamp: Date.now() } ); if (!allowed) { throw new Error("Export not allowed at this time"); } // Proceed with export... }, }); ``` -------------------------------- ### Migrate from IndexedAuthz to Authz Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Replace the deprecated IndexedAuthz class with Authz in v2 and run a backfill mutation. ```typescript // Before (v1) — this import no longer works in v2 // import { IndexedAuthz } from "@djpanda/convex-authz"; // const authz = new IndexedAuthz(components.authz, { permissions, roles, tenantId: "my-app" }); // After (v2) — same constructor, just rename the class import { Authz } from "@djpanda/convex-authz"; const authz = new Authz(components.authz, { permissions, roles, tenantId: "my-app" }); ``` ```typescript // one-time migration mutation export const backfillEffectivePermissions = mutation({ args: {}, handler: async (ctx) => { const users = await ctx.db.query("users").collect(); for (const user of users) { await authz.recomputeUser(ctx, String(user._id)); } }, }); ``` -------------------------------- ### Register the component Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Configure the component in your convex.config.ts file to enable it within your Convex application. ```typescript // convex/convex.config.ts import { defineApp } from "convex/server"; import authz from "@djpanda/convex-authz/convex.config"; const app = defineApp(); app.use(authz); export default app; ``` -------------------------------- ### Check Permissions with Authz.can Source: https://context7.com/dbjpanda/convex-authz/llms.txt Perform global or scoped permission checks within Convex query handlers. ```typescript import { query } from "./_generated/server"; import { authz } from "./authz"; export const canReadDocument = query({ args: { userId: v.string(), docId: v.optional(v.string()) }, handler: async (ctx, args) => { // Global permission check const canRead = await authz.can(ctx, args.userId, "documents:read"); // Scoped permission check (e.g., within a specific team) const canReadInTeam = await authz.can( ctx, args.userId, "documents:read", { type: "team", id: "team_123" } ); // Wildcard permissions are automatically matched // If user has "documents:*" they can access "documents:read" return canRead; }, }); ``` -------------------------------- ### Backfill Effective Permissions Migration Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md A one-time migration mutation to backfill the `effective-permissions` table for all existing users after upgrading. ```typescript // one-time migration mutation export const backfillEffectivePermissions = mutation({ args: {}, handler: async (ctx) => { const users = await ctx.db.query("users").collect(); for (const user of users) { await authz.recomputeUser(ctx, String(user._id)); } }, }); ``` -------------------------------- ### Define Global Authz Client Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Centralize permission and role definitions in a single file to be exported and used throughout the application. ```typescript // convex/authz.ts — single source of truth import { Authz, definePermissions, defineRoles } from "@djpanda/convex-authz"; import { components } from "./_generated/api"; const permissions = definePermissions({ documents: { create: true, read: true, update: true, delete: true }, billing: { view: true, manage: true }, settings: { view: true, manage: true }, }); const roles = defineRoles(permissions, { admin: { documents: ["create", "read", "update", "delete"], billing: ["view", "manage"], settings: ["view", "manage"], }, viewer: { documents: ["read"], settings: ["view"], }, }); // Export the single authz client — import this everywhere export const authz = new Authz(components.authz, { permissions, roles, tenantId: "my-app" }); ``` -------------------------------- ### Use Shared Authz Client in Mutations Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Import the shared authz instance to enforce permission checks within mutation handlers. ```typescript // convex/documents.ts — uses the shared client import { mutation } from "./_generated/server"; import { authz } from "./authz"; export const deleteDocument = mutation({ args: { docId: v.id("documents") }, handler: async (ctx, args) => { await authz.require(ctx, userId, "documents:delete"); // ... }, }); ``` -------------------------------- ### User Offboarding Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Methods for cleaning up user data and access. ```APIDOC ## User Offboarding ### Description Methods to handle user offboarding and deprovisioning. ### Methods - **offboardUser(ctx, userId, options?)**: Offboards a user by removing specified attributes, overrides, or relationships. - **deprovisionUser(ctx, userId, options?)**: Performs a full wipe of roles, overrides, attributes, and relationships. ``` -------------------------------- ### Merging App and Tenant Permissions Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Merge your application's permissions with those from the tenant component in `convex/authz.ts`. Ensure you define your app's specific permissions and roles before merging. ```typescript // convex/authz.ts — merge app + component permissions import { Authz, definePermissions, defineRoles } from "@djpanda/convex-authz"; import { TENANTS_PERMISSIONS, TENANTS_ROLES } from "@djpanda/convex-tenants"; import { components } from "./_generated/api"; // Your app's own permissions const appPermissions = { documents: { create: true, read: true, update: true, delete: true }, }; const appRoles = { editor: { documents: ["create", "read", "update"] as const }, }; // Merge with tenant component's permissions const permissions = definePermissions(appPermissions, TENANTS_PERMISSIONS); const roles = defineRoles(permissions, appRoles, TENANTS_ROLES); export const authz = new Authz(components.authz, { permissions, roles, tenantId: "my-app" }); ``` -------------------------------- ### Attribute and Override Management Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md Methods for managing user attributes and permission overrides. ```APIDOC ## Attribute and Override Management ### Description Manage custom user attributes and explicit permission grants or denials. ### Methods - **setAttribute(ctx, userId, key, value, actorId?)**: Sets a user attribute. - **removeAttribute(ctx, userId, key, actorId?)**: Removes a user attribute. - **getUserAttributes(ctx, userId)**: Retrieves all attributes for a user. - **grantPermission(ctx, userId, permission, scope?, reason?, expiresAt?, actorId?)**: Explicitly grants a permission. - **denyPermission(ctx, userId, permission, scope?, reason?, expiresAt?, actorId?)**: Explicitly denies a permission. ``` -------------------------------- ### Observe user roles with useUserRoles Source: https://context7.com/dbjpanda/convex-authz/llms.txt Retrieves and observes a user's roles in real-time. It handles both global and scoped role lookups. ```tsx import { useUserRoles } from "@djpanda/convex-authz/react"; function UserRoleDisplay({ userId, teamId }: { userId?: string; teamId?: string }) { // Get all roles (uses defaultUserId if userId not provided) const { roles, isLoading, error } = useUserRoles({ userId }); // Get scoped roles const { roles: teamRoles } = useUserRoles({ userId, scope: teamId ? { type: "team", id: teamId } : undefined, }); if (isLoading) return ; if (error) return ; return (

User Roles

    {roles.map((r: any) => (
  • {r.role} {r.scope ? `(${r.scope.type}: ${r.scope.id})` : "(global)"}
  • ))}
); } ``` -------------------------------- ### Define Permissions and Roles Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Define custom permissions and roles for your application using the Authz client. This involves defining granular permissions and then assigning them to roles. ```typescript // convex/authz.ts import { Authz, definePermissions, defineRoles } from "@djpanda/convex-authz"; import { components } from "./_generated/api"; // Step 1: Define permissions const permissions = definePermissions({ documents: { create: true, read: true, update: true, delete: true, }, settings: { view: true, manage: true, }, }); // Step 2: Define roles const roles = defineRoles(permissions, { admin: { documents: ["create", "read", "update", "delete"], settings: ["view", "manage"], }, editor: { documents: ["create", "read", "update"], settings: ["view"], }, viewer: { documents: ["read"], }, }); // Step 3: Create the authz client export const authz = new Authz(components.authz, { permissions, roles, tenantId: "my-app" }); ``` -------------------------------- ### Passing Authz Client to Tenants API Source: https://github.com/dbjpanda/convex-authz/blob/main/skills/convex-authz/SKILL.md When initializing the tenants API in `convex/tenants.ts`, pass the shared `authz` client. This ensures consistent authorization across your application and integrated components. ```typescript // convex/tenants.ts — pass the shared authz client import { makeTenantsAPI } from "@djpanda/convex-tenants"; import { components } from "./_generated/api"; import { authz } from "./authz"; export const { createOrg, inviteMember, removeMember, // ... } = makeTenantsAPI(components.tenants, { authz, creatorRole: "owner", auth: async (ctx) => { // return the current user ID }, }); ``` -------------------------------- ### Argument Validation: permission format Source: https://github.com/dbjpanda/convex-authz/blob/main/README.md Validates that the permission follows the 'resource:action' format, such as 'documents:read'. Incorrect formats will trigger an error. ```plaintext permission Must be `resource:action` (e.g. `documents:read`) "Invalid permission format: \"read\". Expected \"resource:action\"" ```