| name | clerk-enterprise-rbac |
| description | 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 SAML".
|
| allowed-tools | Read, Write, Edit, Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Clerk Enterprise RBAC
Overview
Implement enterprise-grade SSO, role-based access control, and organization management.
Prerequisites
- Clerk Enterprise tier subscription
- Identity Provider (IdP) with SAML/OIDC support
- Understanding of role-based access patterns
- Organization structure defined
Instructions
Step 1: Configure SAML SSO
In Clerk Dashboard
- Go to Configure > SSO Connections
- Add SAML Connection
- Configure IdP settings:
- ACS URL:
https://clerk.yourapp.com/v1/saml
- Entity ID: Provided by Clerk
- Download SP metadata
IdP Configuration (Example: Okta)
<saml:Attribute Name="email">
<saml:AttributeValue>user.email</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="firstName">
<saml:AttributeValue>user.firstName</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="lastName">
<saml:AttributeValue>user.lastName</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="role">
<saml:AttributeValue>user.role</saml:AttributeValue>
</saml:Attribute>
Step 2: Define Roles and Permissions
export const PERMISSIONS = {
'users:read': 'View user list',
'users:write': 'Create/update users',
'users:delete': 'Delete users',
'settings:read': 'View settings',
'settings:write': 'Modify settings',
'billing:read': 'View billing info',
'billing:write': 'Manage billing',
'reports:read': 'View reports',
'reports:export': 'Export reports'
} as const
export type Permission = keyof typeof PERMISSIONS
export const ROLES = {
'org:admin': [
'users:read', 'users:write', 'users:delete',
'settings:read', 'settings:write',
'billing:read', 'billing:write',
'reports:read', 'reports:export'
],
'org:manager': [
'users:read', 'users:write',
'settings:read',
,
],
: [
,
],
: [
]
} <, []>
= keyof
Step 3: Permission Checking
import { auth } from '@clerk/nextjs/server'
import { ROLES, Permission, Role } from './permissions'
export async function hasPermission(permission: Permission): Promise<boolean> {
const { orgRole } = await auth()
if (!orgRole) return false
const role = orgRole as Role
const rolePermissions = ROLES[role]
if (!rolePermissions) return false
return rolePermissions.includes(permission)
}
export async function requirePermission(permission: Permission): Promise<void> {
const allowed = await hasPermission(permission)
if (!allowed) {
throw new Error(`Permission denied: `)
}
}
() {
(): < <>> {
(: ) => {
allowed = (permission)
(!allowed) {
.(
{ : , : permission },
{ : }
)
}
(req)
}
}
}
Step 4: Protected Routes with RBAC
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'
const isPublicRoute = createRouteMatcher(['/', '/sign-in(.*)', '/sign-up(.*)'])
const isAdminRoute = createRouteMatcher(['/admin(.*)'])
const isBillingRoute = createRouteMatcher(['/billing(.*)'])
export default clerkMiddleware(async (auth, request) => {
const { userId, orgRole } = await auth()
if (isPublicRoute(request)) {
return NextResponse.next()
}
if (!userId) {
return auth.redirectToSignIn()
}
if (isAdminRoute(request)) {
if (orgRole !== 'org:admin') {
return NextResponse.redirect(new URL('/unauthorized', request.url))
}
}
((request)) {
(![, ].(orgRole || )) {
.( (, request.))
}
}
.()
})
Step 5: Organization Management
import { clerkClient, auth } from '@clerk/nextjs/server'
export async function createOrganization(name: string, slug: string) {
const { userId } = await auth()
const client = await clerkClient()
const org = await client.organizations.createOrganization({
name,
slug,
createdBy: userId!
})
return org
}
export async function inviteToOrganization(
orgId: string,
email: string,
role: string
) {
const client = await clerkClient()
const invitation = await client.organizations.createOrganizationInvitation({
organizationId: orgId,
emailAddress: email,
role,
inviterUserId: (await auth()).userId!
})
return invitation
}
export () {
client = ()
client..({
: orgId,
userId,
role
})
}
() {
client = ()
{ : members } = client..({
: orgId
})
members
}
Step 6: React Components with RBAC
'use client'
import { useAuth, useOrganization } from '@clerk/nextjs'
import { ROLES, Permission, Role } from '@/lib/permissions'
interface PermissionGateProps {
permission: Permission
children: React.ReactNode
fallback?: React.ReactNode
}
export function PermissionGate({
permission,
children,
fallback = null
}: PermissionGateProps) {
const { orgRole } = useAuth()
if (!orgRole) return fallback
const role = orgRole as Role
const permissions = ROLES[role] || []
if (!permissions.includes(permission)) {
return fallback
}
return <>{children}</>
}
function AdminPanel() {
return (
<div>
Dashboard
Add User
Contact admin for settings access}
>
)
}
Step 7: API Route Protection
import { auth } from '@clerk/nextjs/server'
import { hasPermission } from '@/lib/auth-permissions'
export async function GET() {
const { userId, orgId } = await auth()
if (!userId || !orgId) {
return Response.json({ error: 'Unauthorized' }, { status: 401 })
}
if (!await hasPermission('users:read')) {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
const users = await db.user.findMany({
where: { organizationId: orgId }
})
return Response.json(users)
}
export async function POST(request: Request) {
const { userId, orgId } = ()
(!userId || !orgId) {
.({ : }, { : })
}
(! ()) {
.({ : }, { : })
}
data = request.()
user = db..({
: {
...data,
: orgId,
: userId
}
})
.(user)
}
SSO Configuration Matrix
| IdP | Protocol | Setup Guide |
|---|
| Okta | SAML 2.0 | Clerk Dashboard > SSO |
| Azure AD | OIDC/SAML | Clerk Dashboard > SSO |
| Google Workspace | OIDC | Clerk Dashboard > SSO |
| OneLogin | SAML 2.0 | Clerk Dashboard > SSO |
Output
- SAML SSO configured
- Roles and permissions defined
- RBAC enforcement in middleware
- Organization management
Error Handling
| Error | Cause | Solution |
|---|
| SSO login fails | Misconfigured IdP | Check attribute mapping |
| Permission denied | Missing role | Review role assignments |
| Org not found | User not in org | Prompt org selection |
Resources
Next Steps
Proceed to clerk-migration-deep-dive for auth provider migration.