### 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 (