关于 clerk-orgs
面向构建 B2B 或多租户应用的开发者,介绍 Clerk Organizations 的启用、组织成员、角色权限和企业 SSO 模式。还涵盖通过 CLI 与 API 管理组织配置及成员。
主要功能
- 01组织启用与成员模式选择
- 02组织成员和邀请管理
- 03角色与权限访问控制
- 04企业 SSO 与域名验证
- 05CLI 和 API 组织管理
适用场景
- 01为 B2B 应用配置组织与成员关系
- 02根据组织角色或权限限制访问
- 03通过 CLI 创建组织并管理邀请
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ clerk/ skills --skill clerk-orgs安装后在 Claude Code 中输入/clerk-orgs确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/enterprise-sso.mdinvitations.mdnextjs-patterns.mdroles-permissions.md详细介绍
组织(B2B SaaS)
停止——前置条件。 必须先启用组织,任何与组织相关的 API、钩子或组件才能生效。两种方式:(1)Dashboard → Organizations settings,或(2)
clerk enable orgs(请参阅下文“Agent 优先:以编程方式管理组织”)。请慎重选择 Membership 模式:Membership required(自 2025-08-22 起为默认设置)会将已登录用户引导至choose-organization任务,并禁用个人账户;Membership optional则保留个人账户,以支持 B2C 与 B2B 并存。如果需要同时提供个人订阅和组织订阅,请选择optional。版本:本技能面向当前 SDK(
@clerk/nextjsv7+、@clerk/reactv6+ — Core 3)。Core 2 的差异会以内联提示> **仅适用于 Core 2(当前 SDK 请跳过):**标出——完整版本表请参阅clerk技能。
此应用是否应该使用组织?
启用任何功能前请先做出判断。检查项目、统计信号,然后询问开发者——不要悄悄启用组织。
表明此应用为多租户应用的信号(建议使用组织):
- 用于对用户分组的模型:
Team、Workspace、Tenant、Company、Account - 大多数表都使用相同的外键:
workspace_id、team_id、account_id - 按租户划分范围的路由:
/[workspace]、/[slug]/settings、/t/:tenantId - UI 文案或 README 中出现“邀请你的团队”、“成员”、“席位”或“管理员”
- 存在
*_members联接表,或手动实现的role/permission列 - 按席位或按公司定价
较弱的信号——足以提出问题,但不足以做出判断:
- 个人用户之间的共享或协作:以两个用户 ID 为键的
Share、Collaborator或SharedWith表 - 存在
role或permission列,但没有对应的租户范围 - 文案中出现“协作者”、“与你共享”或“邀请朋友”
表明此应用为单用户应用的信号(不建议使用):
- 每张表都关联到
user_id,没有更高层级的容器,也没有共享功能 - 完全没有邀请、成员资格或共享的概念
- 按个人定价,或没有定价
行动方式:
| 发现情况 | 应采取的行动 |
|---|---|
| 至少 2 个强信号 | 建议使用组织,并指出你发现的信号。新应用:clerk init --template b2b-saas。现有应用:clerk enable orgs。 |
| 1 个强信号,或任何较弱信号 | 将其作为一个选项提出,说明其中的取舍,并让开发者决定。个人用户之间的共享不属于多租户——指向两个用户 ID 的 Share 表并没有租户——但产品往往会从这里发展出多租户需求。 |
| 以上情况都没有 | 不要提起。 |
未经询问,绝不要启用组织。 启用组织还会开启 Membership required,将每位已登录用户都引导至组织选择,并禁用个人账户。对于同时服务个人用户的任何应用,这都是错误的选择。如果应用两者都需要,应使用 Membership optional。
快速开始
- 启用组织——通过 Dashboard → Organizations settings 或
clerk enable orgs(请参阅“Agent 优先”一节)。选择Membership required(仅限 B2B)或Membership optional(B2C + B2B)。 - 创建组织——通过
<OrganizationSwitcher />、<CreateOrganization />,或以编程方式调用clerkClient().organizations.createOrganization()。 - 保护路由——从
auth()读取orgId/orgSlug,并使用has({ role })或has({ permission })进行访问控制。 - 管理成员——通过后端 API 或内置的
<OrganizationProfile />标签页发送邀请。 - 限制成员人数——在创建组织时设置
maxAllowedMemberships,或选择限制席位数的 Billing Plan(请参阅clerk-billing技能)。
您需要什么?
| 任务 | 参考资料 |
|---|---|
| 系统权限目录、自定义角色、角色集 | references/roles-permissions.md |
| 邀请生命周期(创建、列出、撤销、内置 UI) | references/invitations.md |
| 企业 SSO 设置、提供商字段访问、域名验证 | references/enterprise-sso.md |
| 面向组织的 Next.js 适配(逐页角色/权限检查、slug 不变量、按 orgId 限定范围的写入) | references/nextjs-patterns.md |
参考资料
| 参考资料 | 说明 |
|---|---|
references/roles-permissions.md | 默认角色和自定义角色、系统权限目录、权限命名 |
references/invitations.md | 邀请的后端 API 和内置 UI |
references/enterprise-sso.md | 每个组织的 SAML/OIDC、域名验证、正确的字段访问方式 |
references/nextjs-patterns.md | 专为组织设计的 Next.js 适配。通用 Next.js 模式请参阅 clerk-nextjs-patterns 技能。 |
Dashboard 快捷方式
| 操作 | URL |
|---|---|
| 启用 Organizations 和 Membership 模式 | https://dashboard.clerk.com/last-active?path=organizations-settings |
| 管理角色和权限 | https://dashboard.clerk.com/last-active?path=organizations-settings/roles |
| 创建/编辑组织 | https://dashboard.clerk.com/last-active?path=organizations |
| 组织事件的 Webhooks | https://dashboard.clerk.com/last-active?path=webhooks |
Agent 优先:以编程方式管理组织
组织设置(启用开关、成员上限、管理员删除、域名)可通过 PLAPI Instance Config 进行修补。组织的 CRUD、成员关系和邀请由 BAPI 管理。适用于 Agent 创建组织、在多个实例间复制设置或对组织结构进行版本控制。
前置条件:已关联项目(clerk auth login 和 clerk link,参阅 clerk-cli)——或者通过 clerk init 创建的未认领应用:无需登录即可使用 clerk enable orgs 和通过 clerk api 执行组织 CRUD。
通过 CLI 启用 Organizations 和相关设置
clerk enable orgs
如需设置其他选项(成员上限、已验证域名、管理员删除),请修补实例配置:
clerk api --platform PATCH /v1/platform/applications/<app_id>/instances/<ins_id>/config \
-d '{"organization_settings":{"max_allowed_memberships":50,"domains_enabled":true,"admin_delete_enabled":true}}'
创建/列出/删除组织(BAPI)
# Create:
clerk api -X POST /v1/organizations \
-d '{"name":"Acme","slug":"acme","created_by":"user_xxx","max_allowed_memberships":10}'
# List:
clerk api '/v1/organizations?limit=20'
# Get one:
clerk api /v1/organizations/<org_id>
# Update:
clerk api -X PATCH /v1/organizations/<org_id> -d '{"name":"Acme Inc."}'
# Delete:
clerk api -X DELETE /v1/organizations/<org_id>
成员关系
# Add a user to an org:
clerk api -X POST /v1/organizations/<org_id>/memberships \
-d '{"user_id":"user_xxx","role":"org:admin"}'
# List members:
clerk api '/v1/organizations/<org_id>/memberships?limit=50'
# Update role:
clerk api -X PATCH /v1/organizations/<org_id>/memberships/<user_id> \
-d '{"role":"org:member"}'
# Remove:
clerk api -X DELETE /v1/organizations/<org_id>/memberships/<user_id>
邀请
# Send:
clerk api -X POST /v1/organizations/<org_id>/invitations \
-d '{"email_address":"[email protected]","role":"org:member","redirect_url":"https://app.com/accept"}'
# List pending:
clerk api '/v1/organizations/<org_id>/invitations?status=pending'
# Revoke:
clerk api -X POST /v1/organizations/<org_id>/invitations/<inv_id>/revoke \
-d '{"requesting_user_id":"user_xxx"}'
备注
- 此处处理组织配置和 CRUD。组织的订阅/账单(组织方案、按席位数量定价)通过
clerk-billing技能处理。 - 角色和权限目录可在
references/roles-permissions.md中编辑。创建自定义角色需使用clerk config patch(实例级角色定义)——可参阅 Dashboard 的角色编辑器,了解对应的 UI 操作。 - SSO/已验证域名配置请参阅
references/enterprise-sso.md。
文档
关键模式
示例默认使用 @clerk/nextjs。对于其他框架,将导入替换为 @clerk/react(Vite/CRA)、@clerk/astro/components、@clerk/vue、@clerk/expo、@clerk/react-router 或 @clerk/tanstack-react-start——各 SDK 中的功能级 API(has()、orgId、<OrganizationSwitcher />、<Show>)完全相同。框架特有的模式(中间件、重定向)见 references/nextjs-patterns.md。
1. 从身份验证中读取组织
在服务端访问当前组织:
import { auth } from '@clerk/nextjs/server'
const { orgId, orgSlug, orgRole } = await auth()
if (!orgId) {
// user has no active org — either not in any, or viewing Personal Account
}
auth() 是 Next.js 专用函数。各 SDK 提供的等效服务端访问器包括:auth(event)(Nuxt,通过 event.context.auth())、context.locals.auth()(Astro)、getAuth(req)(Express,在 clerkMiddleware() 之后)。客户端:useAuth()(基于 React 的 SDK)或组合式函数(Vue/Nuxt)。这些方法均返回相同结构的 orgId / orgSlug / orgRole。
2. 使用组织 Slug 的动态路由
按组织设置路由的模式适用于任何支持基于文件的动态路由的框架。Next.js 示例:
app/orgs/[slug]/page.tsx
app/orgs/[slug]/settings/page.tsx
务必确认 URL 中的 slug 与当前组织的 slug 相符,否则用户可能会在会话中的 orgSlug 已过期时访问 /orgs/other-org/...:
import { auth } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'
export default async function OrgPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const { orgSlug } = await auth()
if (orgSlug !== slug) {
redirect('/dashboard') // or whatever your "no-access" flow is
}
return <div>Welcome to {orgSlug}</div>
}
在 Next.js 中,若页面像此处一样使用 redirect() 控制渲染,请在页面旁添加 loading.tsx:启用 Cache Components(create-next-app 的默认设置)时,在 <Suspense> 边界外读取请求数据会导致 next build 失败。
3. 基于角色的访问控制
const { has } = await auth()
if (!has({ role: 'org:admin' })) {
return <div>Admin access required</div>
}
对于你在 Dashboard 中创建的自定义 Permissions,也可以使用相同的 has() 接口检查权限:
if (!has({ permission: 'org:invoices:create' })) {
redirect('/unauthorized')
}
在服务端,has({ permission }) 仅适用于自定义 Permissions:System Permissions(org:sys_*)不会包含在会话令牌中,因此在那里检查它们始终会返回 false。Clerk 文档中,<Show when={{ permission }}> 也仅用于检查自定义 Permissions。若要要求 System Permission,请检查拥有该权限的角色(has({ role: 'org:admin' }))。
权限命名约定。 System Permissions 以 org:sys_ 为前缀;自定义 Permissions 使用 org:<resource>:<action>。完整的 System Permissions 目录位于 references/roles-permissions.md,简要列表如下:
org:sys_memberships:{read, manage}org:sys_profile:{manage, delete}org:sys_domains:{read, manage}org:sys_billing:{read, manage}
切勿编造 org:create、org:manage_members、org:update_metadata 之类的名称——它们不是有效的权限 slug。自定义角色和权限表请参阅 references/roles-permissions.md。
4. 使用 <Show> 进行条件渲染
在 Next.js Server Component 中,<Show> 会从请求中读取会话,因此应将它放在 <Suspense> 中。在 Client Component 中,它会读取客户端身份验证状态,无需使用边界。
import { Show } from '@clerk/nextjs'
import { Suspense } from 'react'
<Suspense>
<Show when={{ role: 'org:admin' }}>
<AdminPanel />
</Show>
<Show when={{ permission: 'org:invoices:create' }}>
<NewInvoiceButton />
</Show>
</Suspense>
仅限 Core 2(当前 SDK 请跳过): 使用
<Protect role="org:admin">/<Protect permission="...">代替<Show>。在 Core 3 中,<Show>取代了<Protect>和<SignedIn>/<SignedOut>。
同一组件在 Astro 模板中的语法(从 @clerk/astro/components 导入):
<Show when={{ role: 'org:admin' }}>
<AdminPanel />
</Show>
5. OrganizationSwitcher
import { OrganizationSwitcher } from '@clerk/nextjs'
<OrganizationSwitcher
hidePersonal
afterCreateOrganizationUrl="/orgs/:slug/dashboard"
afterSelectOrganizationUrl="/orgs/:slug/dashboard"
/>
主要属性:
hidePersonal: boolean— 隐藏 Personal Account 选项。默认值为false。仅面向 B2B 的应用请传入true。afterCreateOrganizationUrl、afterSelectOrganizationUrl、afterLeaveOrganizationUrl、afterSelectPersonalUrl— 导航钩子。:slug会在运行时替换。createOrganizationMode、organizationProfileMode—'modal' | 'navigation'(默认值为'modal')。
完整属性列表请参阅组件参考。
6. 会话任务 — 选择组织
启用 Membership required(默认设置)时,没有组织的用户登录后会进入 choose-organization 会话任务。Clerk 会在 <SignIn /> 中自动处理此流程,但你也可以自行托管该界面:
import { ClerkProvider } from '@clerk/nextjs'
<ClerkProvider taskUrls={{ 'choose-organization': '/session-tasks/choose-organization' }}>
{children}
</ClerkProvider>
// app/session-tasks/choose-organization/page.tsx
import { TaskChooseOrganization } from '@clerk/nextjs'
export default function Page() {
return <TaskChooseOrganization redirectUrlComplete="/dashboard" />
}
TaskChooseOrganization 作为导入式组件提供给基于 React 的 SDK(@clerk/nextjs、@clerk/react、@clerk/react-router、@clerk/tanstack-react-start)。对于 JS Frontend SDK(@clerk/clerk-js),对应的方法是 clerk.mountTaskChooseOrganization(node) / clerk.unmountTaskChooseOrganization(node)。
仅限 Core 2(当前 SDK 请跳过): 不支持会话任务。若要强制用户在登录时选择组织,请将其重定向到一个渲染
<OrganizationSwitcher hidePersonal />的页面。
默认角色 + 系统权限
| 角色 | 默认含义 |
|---|---|
org:admin | 完全访问权限——拥有所有系统权限,可管理组织和成员资格 |
org:member | 仅可读取成员和账单权限 |
你可以在 Dashboard → Organizations → Roles & Permissions 中为每个实例创建最多 10 个自定义角色。每个组织的角色由 Role Sets 控制——完整模型(自定义角色、Creator/Default 角色设置、角色集和系统权限目录)请参阅 references/roles-permissions.md。
账单检查
启用 Clerk Billing 后,has() 还支持计划和功能检查:
const { has } = await auth()
has({ plan: 'gold' }) // subscription plan
has({ feature: 'widgets' }) // feature entitlement
仅限 Core 2(当前 SDK 请跳过):
has()仅支持role和permission。不支持账单检查。
完整的账单功能和席位限制计划模型请参阅 clerk-billing。
企业 SSO
按组织配置 SAML/OIDC。在 Dashboard → Configure → Enterprise Connections 中配置(或按组织配置:Organizations → 选择组织 → SSO Connections)。SSO 连接直接关联其域名;无需单独的 Verified Domain(且同一域名不能同时使用这两项功能)。首次 SSO 登录时自动加入组织使用的是 JIT Provisioning,而非 Verified Domains。重要提示:provider 字段位于 enterpriseConnection 上,而不是直接位于 enterpriseAccounts[0] 上。完整流程和正确的字段访问方式请参阅 references/enterprise-sso.md。
// Strategy name for Enterprise SSO (Core 3)
strategy: 'enterprise_sso'
仅限 Core 2(当前 SDK 请跳过): 使用
strategy: 'saml'和user.samlAccounts,而非user.enterpriseAccounts。
注意事项
maxAllowedMemberships 限制席位数
const clerk = await clerkClient()
await clerk.organizations.createOrganization({
name: 'Acme Corp',
createdBy: userId,
maxAllowedMemberships: 10,
})
// Update later:
await clerk.organizations.updateOrganization(orgId, {
maxAllowedMemberships: 25,
})
如需与订阅绑定的分级席位限制,请使用有席位限制的 Billing Plan(参阅 clerk-billing)。
Billing 在功能层级控制权限
启用 Clerk Billing 后,如果与该权限关联的 Feature 未包含在组织当前生效的 Plan 中,has({ permission: 'org:posts:edit' }) 将返回 false——即使用户已通过其角色获得该权限。请确保在 Dashboard → Billing → Plans → Features 中将该 Feature 添加到当前生效的 Plan。
元数据更新会替换内容,而非合并
updateOrganization({ publicMetadata }) 会覆盖所有公开元数据。请先读取,再展开,最后写入:
const org = await clerk.organizations.getOrganization({ organizationId: orgId })
await clerk.organizations.updateOrganization(orgId, {
publicMetadata: { ...org.publicMetadata, newField: 'value' },
})
这同样适用于 privateMetadata,以及通过 clerkClient.users.updateUser 更新用户元数据的情况。
错误特征(快速诊断)
大多数“与组织相关”的故障源于配置,而非代码。在检查以下事项之前,请勿修改组件:
| 错误 / 症状 | 根本原因 | 修复方法 |
|---|---|---|
已登录用户的 orgId / orgSlug 为 undefined | 此实例未启用组织,或用户没有活跃组织(个人账户) | 在 Dashboard → Organizations 中启用;检查 Membership mode;显示 <OrganizationSwitcher /> |
has({ permission: ... }) 始终为 false | 使用了虚构的 slug(org:manage_members),或使用了 has() 无法检查的系统权限(org:sys_*) | 检查角色(has({ role: 'org:admin' })),或在 Dashboard 中创建自定义权限(参见 roles-permissions.md) |
has({ role }) 返回 false,但用户看起来像管理员 | 角色更改后,会话令牌已过期 | 重新登录,或刷新会话:await clerk.session?.reload() |
即使已分配角色,has({ permission }) 仍返回 false | 功能未关联到当前套餐(Billing 会控制权限) | Dashboard → Billing → Plans → 关联功能 |
<OrganizationSwitcher /> 未显示“个人账户” | 已启用 Membership required 模式(自 2025 年 8 月 22 日起为默认设置) | Dashboard → Organizations settings → Membership optional |
TaskChooseOrganization 抛出“cannot render when a user doesn't have current session tasks”错误 | 在 choose-organization 任务上下文之外渲染 | 仅在 choose-organization 会话任务路由中使用;不要无条件渲染 |
enterpriseAccounts[0].provider 为 undefined | 在错误的嵌套层级访问 provider | 使用 user.enterpriseAccounts[0].enterpriseConnection?.provider |
授权模式(完整示例)
保护 slug 作用域管理页面的服务端组件:
import { auth } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'
export default async function AdminPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const { orgSlug, has } = await auth()
if (orgSlug !== slug) redirect('/dashboard')
if (!has({ role: 'org:admin' })) redirect(`/orgs/${orgSlug}`)
return <div>Admin settings for {orgSlug}</div>
}
在其旁边添加 loading.tsx,以便 next build 能在 Cache Components 下通过。
有关使用 auth.protect() 进行页面级角色和权限检查(Next.js),请参阅 references/nextjs-patterns.md。
邀请(简版)
从服务端操作或路由处理程序发送:
import { clerkClient, auth } from '@clerk/nextjs/server'
export async function inviteMember(emailAddress: string, role: string) {
const { userId, orgId, has } = await auth()
if (!userId || !orgId) throw new Error('No active organization')
// Inviting needs the System Permission org:sys_memberships:manage, which
// has() can't check, so check a role that carries it.
if (!has({ role: 'org:admin' })) {
throw new Error('Not authorized to invite members')
}
const clerk = await clerkClient()
return clerk.organizations.createOrganizationInvitation({
organizationId: orgId, // the active org that has() checked
inviterUserId: userId, // required per Backend API
emailAddress,
role, // e.g. 'org:admin' or 'org:member'
redirectUrl: 'https://yourapp.com/accept-invite',
})
}
完整生命周期(列表、撤销、批量创建、内置 <OrganizationProfile /> UI)请参阅 references/invitations.md。
工作流
- 启用 — 在 Dashboard 中启用 Organizations + Membership mode
- 创建组织 — 通过 UI 组件或 Backend API
- 邀请成员 — 通过 Backend API 或内置 UI,并提供
inviterUserId - 控制访问 — 使用
has({ role }),或使用自定义权限调用has({ permission })。has()无法检查系统权限(org:sys_*) - 限定路由范围 — 在每个受保护页面上检查
orgSlug === params.slug - 切换组织 —
<OrganizationSwitcher />会处理整个流程
另请参阅
clerk-setup— Clerk 初始安装clerk-billing— 席位限制套餐、按套餐计费、has({ plan })/has({ feature })clerk-webhooks— 将组织事件同步到数据库(organization.created、organizationMembership.*)clerk-backend-api— 完整的 Backend API 参考clerk-nextjs-patterns— 特定框架的中间件、服务端操作、缓存