clerk-enterprise-rbac
Configure enterprise SSO, role-based access control, and organization management. Use when implementing SSO integration, configuring role-based permissions, or setting up organization-level controls. Trigger with phrases like "clerk SSO", "clerk RBAC", "clerk enterprise", "clerk roles", "clerk permissions", "clerk organizations".
Best use case
clerk-enterprise-rbac is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Configure enterprise SSO, role-based access control, and organization management. Use when implementing SSO integration, configuring role-based permissions, or setting up organization-level controls. Trigger with phrases like "clerk SSO", "clerk RBAC", "clerk enterprise", "clerk roles", "clerk permissions", "clerk organizations".
Teams using clerk-enterprise-rbac should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/clerk-enterprise-rbac/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How clerk-enterprise-rbac Compares
| Feature / Agent | clerk-enterprise-rbac | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Configure enterprise SSO, role-based access control, and organization management. Use when implementing SSO integration, configuring role-based permissions, or setting up organization-level controls. Trigger with phrases like "clerk SSO", "clerk RBAC", "clerk enterprise", "clerk roles", "clerk permissions", "clerk organizations".
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Clerk Enterprise RBAC
## Overview
Implement enterprise-grade role-based access control, organization management, and SSO with Clerk. Covers custom roles and permissions, organization lifecycle, multi-tenant access patterns, SAML/OIDC SSO, and the Backend API for programmatic role management (released Nov 2025).
## Prerequisites
- Clerk Pro or Enterprise plan (Organizations + SSO require paid plan)
- Organizations feature enabled in Clerk Dashboard > Organizations > Settings
- Next.js 14+ with App Router (examples use `@clerk/nextjs`)
## Instructions
### Step 1: Enable Organizations and Add UI Components
```typescript
// app/org-selector/page.tsx
import { OrganizationSwitcher, OrganizationProfile } from '@clerk/nextjs'
export default function OrgPage() {
return (
<div className="p-8">
<h1>Select Organization</h1>
<OrganizationSwitcher
hidePersonal={false}
afterSelectOrganizationUrl="/dashboard"
afterCreateOrganizationUrl="/dashboard"
/>
<div className="mt-8">
<OrganizationProfile />
</div>
</div>
)
}
```
### Step 2: Define Custom Roles and Permissions
Configure in **Clerk Dashboard > Organizations > Roles** and **Permissions**.
**Default roles (built-in):**
| Role | Key | Built-in Permissions |
|------|-----|---------------------|
| Admin | `org:admin` | Full org management (members, settings, billing) |
| Member | `org:member` | View org, read-only access |
**Custom permissions (create in Dashboard > Organizations > Permissions):**
| Permission | Key | Description |
|------------|-----|-------------|
| Read data | `org:data:read` | View organization resources |
| Write data | `org:data:write` | Create/update resources |
| Delete data | `org:data:delete` | Delete resources |
| Manage billing | `org:billing:manage` | Access billing settings |
| View analytics | `org:analytics:read` | Access analytics dashboard |
**Custom roles (create in Dashboard > Organizations > Roles):**
| Role | Permissions | Use Case |
|------|-------------|----------|
| `org:manager` | `data:read`, `data:write`, `analytics:read` | Content managers |
| `org:viewer` | `data:read` | Read-only stakeholders |
| `org:billing_admin` | `data:read`, `billing:manage` | Finance team |
### Step 3: RBAC Middleware — Route Protection by Role
```typescript
// middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher([
'/',
'/sign-in(.*)',
'/sign-up(.*)',
'/api/webhooks(.*)',
])
const isAdminRoute = createRouteMatcher(['/admin(.*)'])
const isManagerRoute = createRouteMatcher(['/manage(.*)'])
export default clerkMiddleware(async (auth, req) => {
if (isPublicRoute(req)) return
if (isAdminRoute(req)) {
// Only org:admin can access /admin/*
await auth.protect({ role: 'org:admin' })
} else if (isManagerRoute(req)) {
// org:admin OR org:manager can access /manage/*
await auth.protect((has) =>
has({ role: 'org:admin' }) || has({ role: 'org:manager' })
)
} else {
// All other routes just require authentication
await auth.protect()
}
})
```
### Step 4: Permission Checks in Server Components
```typescript
// app/admin/page.tsx
import { auth } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'
export default async function AdminPage() {
const { userId, orgId, orgRole, has } = await auth()
if (!userId) redirect('/sign-in')
if (!orgId) redirect('/org-selector')
// Permission-based checks (preferred over role-based)
const canManageMembers = has({ permission: 'org:sys_memberships:manage' })
const canWriteData = has({ permission: 'org:data:write' })
const canDeleteData = has({ permission: 'org:data:delete' })
const canViewAnalytics = has({ permission: 'org:analytics:read' })
return (
<div>
<h1>Admin Panel</h1>
<p>Current role: {orgRole}</p>
<nav>
{canManageMembers && <a href="/admin/members">Manage Members</a>}
{canWriteData && <a href="/admin/content">Content Management</a>}
{canDeleteData && <a href="/admin/danger-zone">Danger Zone</a>}
{canViewAnalytics && <a href="/admin/analytics">Analytics</a>}
</nav>
</div>
)
}
```
### Step 5: Permission Checks in Client Components
```typescript
'use client'
import { Protect, useOrganization, useAuth } from '@clerk/nextjs'
export function AdminSection() {
const { organization } = useOrganization()
const { has } = useAuth()
return (
<div>
<h2>{organization?.name}</h2>
{/* Declarative: Protect component with fallback */}
<Protect
role="org:admin"
fallback={<p>You need admin access to view this section.</p>}
>
<DangerZone />
</Protect>
{/* Permission-based rendering */}
<Protect permission="org:data:write">
<EditForm />
</Protect>
{/* Imperative: has() for conditional logic */}
{has?.({ permission: 'org:analytics:read' }) && (
<AnalyticsDashboard />
)}
</div>
)
}
```
### Step 6: Organization Member Management via Backend API
```typescript
// app/api/org/members/route.ts
import { auth, clerkClient } from '@clerk/nextjs/server'
export async function GET() {
const { orgId, has } = await auth()
if (!orgId) return Response.json({ error: 'No org selected' }, { status: 400 })
if (!has({ permission: 'org:sys_memberships:read' })) {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
const client = await clerkClient()
const members = await client.organizations.getOrganizationMembershipList({
organizationId: orgId,
})
return Response.json({
members: members.data.map(m => ({
userId: m.publicUserData?.userId,
name: `${m.publicUserData?.firstName} ${m.publicUserData?.lastName}`,
email: m.publicUserData?.identifier,
role: m.role,
joinedAt: m.createdAt,
})),
})
}
export async function POST(req: Request) {
const { orgId, userId, has } = await auth()
if (!orgId || !has({ permission: 'org:sys_memberships:manage' })) {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
const { emailAddress, role } = await req.json()
const client = await clerkClient()
const invitation = await client.organizations.createOrganizationInvitation({
organizationId: orgId,
emailAddress,
role: role || 'org:member',
inviterUserId: userId!,
})
return Response.json({ invitation: { id: invitation.id, emailAddress, role } })
}
```
### Step 7: Programmatic Role/Permission Management (Backend API)
```typescript
// lib/org-roles.ts — manage roles and permissions via API (released Nov 2025)
import { clerkClient } from '@clerk/nextjs/server'
export async function createCustomRole(orgId: string) {
const client = await clerkClient()
// Create a custom permission
await client.organizations.createOrganizationPermission({
organizationId: orgId,
name: 'Manage reports',
key: 'org:reports:manage',
description: 'Create, edit, and delete reports',
})
// Create a custom role with that permission
await client.organizations.createOrganizationRole({
organizationId: orgId,
name: 'Report Manager',
key: 'org:report_manager',
description: 'Can manage all reports',
permissions: ['org:reports:manage', 'org:data:read'],
})
}
// Update a member's role
export async function updateMemberRole(
orgId: string,
userId: string,
newRole: string
) {
const client = await clerkClient()
const memberships = await client.organizations.getOrganizationMembershipList({
organizationId: orgId,
})
const membership = memberships.data.find(
m => m.publicUserData?.userId === userId
)
if (!membership) throw new Error('User is not a member of this organization')
await client.organizations.updateOrganizationMembership({
organizationId: orgId,
userId,
role: newRole,
})
}
```
### Step 8: SAML SSO Configuration
Configure in **Clerk Dashboard > SSO Connections > Add SAML Connection**:
1. **ACS URL:** `https://<your-clerk-frontend-api>.clerk.accounts.dev/v1/saml/acs`
2. **Entity ID:** `https://<your-clerk-frontend-api>.clerk.accounts.dev/v1/saml/metadata`
3. Upload IdP metadata XML from your provider (Okta, Azure AD, Google Workspace)
4. Map SAML attributes: `email`, `firstName`, `lastName`
```typescript
// Enforce SSO for specific email domains
// Clerk Dashboard > Organizations > Settings > "Verified domains"
// Add your company domain (e.g., acme.com)
// Users with @acme.com emails will be forced through SSO
```
## Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| `orgId` is null | No active organization | Redirect to org selector, show `<OrganizationSwitcher />` |
| `has()` returns false | Role/permission not assigned | Check assignment in Dashboard > Organizations > Members |
| Permission denied on middleware | User lacks required role | Verify route matcher maps to correct role |
| SSO login fails | Misconfigured IdP metadata | Verify ACS URL and Entity ID in IdP settings |
| Invitation fails | Email already a member | Check membership before inviting |
| Custom role not visible | Created via API, not Dashboard | Roles created via API are org-scoped, not instance-wide |
## Enterprise Considerations
- Roles and permissions are embedded in the session JWT -- no extra network requests needed for authorization checks
- Custom roles created in the Dashboard are instance-wide; roles created via Backend API are organization-scoped
- For multi-tenant SaaS, combine Organizations with tenant-scoped database queries (`WHERE org_id = :orgId`)
- Session claims include `org_id`, `org_role`, and `org_permissions` -- available in middleware without API calls
- Verified domains + SAML SSO enable "just-in-time provisioning" -- users auto-join the org on first SSO sign-in
- Consider the `org:sys_*` system permissions (`sys_memberships:manage`, `sys_memberships:read`, `sys_domains:manage`) for built-in org management actions
## Resources
- [Organizations Overview](https://clerk.com/docs/guides/organizations/overview)
- [Roles & Permissions](https://clerk.com/docs/guides/organizations/control-access/roles-and-permissions)
- [Check Access](https://clerk.com/docs/guides/organizations/control-access/check-access)
- [Roles/Permissions Backend API](https://clerk.com/changelog/2025-11-24-organization-roles-and-permission-bapi-management)
## Next Steps
Proceed to `clerk-migration-deep-dive` for auth provider migration.Related Skills
kubernetes-rbac-analyzer
Kubernetes Rbac Analyzer - Auto-activating skill for Security Advanced. Triggers on: kubernetes rbac analyzer, kubernetes rbac analyzer Part of the Security Advanced skill category.
exa-enterprise-rbac
Manage Exa API key scoping, team access controls, and domain restrictions. Use when implementing multi-key access control, configuring per-team search limits, or setting up organization-level Exa governance. Trigger with phrases like "exa access control", "exa RBAC", "exa enterprise", "exa team keys", "exa permissions".
evernote-enterprise-rbac
Implement enterprise RBAC for Evernote integrations. Use when building multi-tenant systems, implementing role-based access, or handling business accounts. Trigger with phrases like "evernote enterprise", "evernote rbac", "evernote business", "evernote permissions".
documenso-enterprise-rbac
Configure Documenso enterprise role-based access control and team management. Use when implementing team permissions, configuring organizational roles, or setting up enterprise access controls. Trigger with phrases like "documenso RBAC", "documenso teams", "documenso permissions", "documenso enterprise", "documenso roles".
deepgram-enterprise-rbac
Configure enterprise role-based access control for Deepgram integrations. Use when implementing team permissions, managing API key scopes, or setting up organization-level access controls. Trigger: "deepgram RBAC", "deepgram permissions", "deepgram access control", "deepgram team roles", "deepgram enterprise", "deepgram key scopes".
databricks-enterprise-rbac
Configure Databricks enterprise SSO, Unity Catalog RBAC, and organization management. Use when implementing SSO integration, configuring role-based permissions, or setting up organization-level controls with Unity Catalog. Trigger with phrases like "databricks SSO", "databricks RBAC", "databricks enterprise", "unity catalog permissions", "databricks SCIM".
coreweave-enterprise-rbac
Configure RBAC and namespace isolation for CoreWeave multi-team GPU access. Use when managing team permissions, isolating GPU quotas, or implementing namespace-level access control. Trigger with phrases like "coreweave rbac", "coreweave permissions", "coreweave namespace isolation", "coreweave team access".
cohere-enterprise-rbac
Configure Cohere enterprise API key management, role-based access, and org controls. Use when implementing multi-team API key management, per-team usage limits, or setting up organization-level controls for Cohere. Trigger with phrases like "cohere enterprise", "cohere RBAC", "cohere team keys", "cohere org management", "cohere access control".
coderabbit-enterprise-rbac
Configure CodeRabbit enterprise access control, seat management, and organization policies. Use when managing who gets AI reviews, configuring organization-level defaults, or implementing access policies for CodeRabbit across teams. Trigger with phrases like "coderabbit SSO", "coderabbit RBAC", "coderabbit enterprise", "coderabbit roles", "coderabbit permissions", "coderabbit seats".
clickup-enterprise-rbac
Implement ClickUp Enterprise SSO, OAuth 2.0 multi-workspace access, role-based permissions, and organization management via API v2. Trigger: "clickup SSO", "clickup RBAC", "clickup enterprise", "clickup roles", "clickup permissions", "clickup OAuth app", "clickup multi-workspace".
clickhouse-enterprise-rbac
Configure ClickHouse enterprise RBAC — SQL-based users, roles, row policies, column-level grants, and quota management. Use when setting up multi-user access control, implementing tenant isolation, or configuring enterprise security for ClickHouse. Trigger: "clickhouse RBAC", "clickhouse roles", "clickhouse permissions", "clickhouse row policy", "clickhouse enterprise access", "clickhouse GRANT".
clerk-webhooks-events
Configure Clerk webhooks and handle authentication events. Use when setting up user sync, handling auth events, or integrating Clerk with external systems via Svix webhooks. Trigger with phrases like "clerk webhooks", "clerk events", "clerk user sync", "clerk svix", "clerk event handling".