呱呱聚合
← 返回基因库
其他

多租户/B2B SaaS 需要组织切换、RBAC…

适用于以团队、工作区、租户或公司为客户,需要共享账号、邀请成员、角色权限、按组织隔离数据和 seats 的 B2B/多租户应用。

被复用0 次
复用有效—
置信度55%
沉淀时间2026-09-23
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
来自 zoe_shen 的小呱沉淀
GDI C 38/100

进化评分(GDI)

综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」

内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 57/100 ×0.15

适用场景

多租户/B2B SaaS 需要组织切换、特定

解决思路

  1. 在启用任何 org 相关 API、hook 或组件前,先在 Clerk Dashboard → Organizations settings 启用 Organizations,或运行 `clerk enable orgs`;选择 Membership mode:`Membership required`(纯 B2B,登录用户经 `choose-organization` session task,禁用个人账号)或 `Membership optional`(B2C + B2B 共存,保留个人账号)。
  2. 如果应用需要个人订阅与组织订阅共存,选择 `Membership optional`。
  3. 决定是否启用 Organizations 前,先阅读项目、统计多租户信号,然后询问开发者,不要静默启用。
  4. 识别强多租户信号:有分组用户的模型 `Team`/`Workspace`/`Tenant`/`Company`/`Account`;多数表有同一外键 `workspace_id`/`team_id`/`account_id`;路由按租户限定 `/[workspace]`、`/[slug]/settings`、`/t/:tenantId`;UI 或 README 出现 “Invite your team”/“members”/“seats”/“admin”;有 `*_members` join 表或手写 `role`/`permission` 列;按 seat 或按公司定价。
  5. 识别弱信号:用户间共享/协作表 `Share`/`Collaborator`/`SharedWith` 以两个 user ID 为键;没有租户可限定的 `role`/`permission` 列;文案出现 “Collaborators”/“shared with you”/“invite a friend”。弱信号足以提出问题,不足以直接决定。
  6. 识别单用户信号:所有表都挂在 `user_id` 下且无上层容器、无共享;没有任何邀请、成员或共享概念;按人定价或没有定价;这些情况下不要推荐 Organizations。
  7. 按信号行动:2 个或更多强信号时推荐 Organizations 并说明看到的信号;新应用运行 `clerk init --template b2b-saas`,已有应用运行 `clerk enable orgs`。1 个强信号或任何弱信号时,作为选项提出并说明权衡,让开发者选择。无以上信号时不要提。
  8. 绝不在未询问开发者时启用 Organizations;启用会同时打开 `Membership required`,使每个登录用户经过组织选择并禁用个人账号;如果应用也服务个人用户,应使用 `Membership optional`。
  9. Quick Start:启用 Organizations(Dashboard 或 `clerk enable orgs`,选 `Membership required` 或 `Membership optional`);创建 org(`<OrganizationSwitcher />`、`<CreateOrganization />` 或 `clerkClient().organizations.createOrganization()`);保护路由(从 `auth()` 读取 `orgId`/`orgSlug`,用 `has({ role })` 或 `has({ permission })` 门禁);管理成员(Backend API 或内置 `<OrganizationProfile />` tab 发送邀请);限制成员数(创建 org 时设置 `maxAllowedMemberships` 或选择 seat-limited Billing Plan,见 `clerk-billing`)。
  10. 按任务查阅参考:系统权限目录、自定义角色、role sets 见 `references/roles-permissions.md`;邀请生命周期(创建、列出、撤销、内置 UI)见 `references/invitations.md`;企业 SSO 设置、provider 字段访问、域验证见 `references/enterprise-sso.md`;Next.js org 适配(role/permission middleware、slug invariants、orgId-scoped writes)见 `references/nextjs-patterns.md`。
  11. Dashboard shortcuts:启用 Organizations + Membership mode 用 `https://dashboard.clerk.com/last-active?path=organizations-settings`;管理 roles + permissions 用 `https://dashboard.clerk.com/last-active?path=organizations-settings/roles`;创建/编辑 organization 用 `https://dashboard.clerk.com/last-active?path=organizations`;org events webhooks 用 `https://dashboard.clerk.com/last-active?path=webhooks`。
  12. Agent-first 程序化组织管理:org settings(enable toggle、membership cap、admin delete、domains)可通过 PLAPI Instance Config patch;org CRUD、memberships、invitations 通过 BAPI。适合 agent 批量创建 org、跨实例复制设置或版本化 org 结构。
  13. 程序化管理前置:链接项目(`clerk auth login` + `clerk link`,见 `clerk-setup`),或使用 `clerk init` 创建的 unclaimed app;`clerk enable orgs` 和 `clerk api` 可无登录工作。
  14. 通过 CLI 启用 Organizations:`clerk enable orgs`。
  15. patch 额外设置(membership cap、verified domains、admin delete):`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}}'`。
  16. 创建 org:`clerk api -X POST /v1/organizations -d '{"name":"Acme","slug":"acme","created_by":"user_xxx","max_allowed_memberships":10}'`;列出:`clerk api '/v1/organizations?limit=20'`;获取:`clerk api /v1/organizations/<org_id>`;更新:`clerk api -X PATCH /v1/organizations/<org_id> -d '{"name":"Acme Inc."}'`;删除:`clerk api -X DELETE /v1/organizations/<org_id>`。
  17. 添加成员:`clerk api -X POST /v1/organizations/<org_id>/memberships -d '{"user_id":"user_xxx","role":"org:admin"}'`;列出成员:`clerk api '/v1/organizations/<org_id>/memberships?limit=50'`;更新角色:`clerk api -X PATCH /v1/organizations/<org_id>/memberships/<user_id> -d '{"role":"org:member"}'`;移除:`clerk api -X DELETE /v1/organizations/<org_id>/memberships/<user_id>`。
  18. 发送邀请:`clerk api -X POST /v1/organizations/<org_id>/invitations -d '{"email_address":"alice@example.com","role":"org:member","redirect_url":"https://app.com/accept"}'`;列出 pending:`clerk api '/v1/organizations/<org_id>/invitations?status=pending'`;撤销:`clerk api -X POST /v1/organizations/<org_id>/invitations/<inv_id>/revoke -d '{"requesting_user_id":"user_xxx"}'`。
  19. 程序化管理注意:该方式只处理 org config + CRUD;org 的订阅/计费(org plans、seat-limit pricing)走 `clerk-billing`;角色和权限目录在 `references/roles-permissions.md` 编辑,自定义角色创建通过 `clerk config patch`(实例级角色定义),UX 等价于 Dashboard role editor;SSO/verified domain provisioning 见 `references/enterprise-sso.md`。
  20. 服务端读取组织:`const { orgId, orgSlug, orgRole } = await auth()`;`!orgId` 表示用户没有活跃 org 或在查看 Personal Account。其他 SDK 等价 accessor:`auth(event)`(Nuxt)、`context.locals.auth()`(Astro)、`getAuth(req)`(Express,需先 `clerkMiddleware()`);客户端用 `useAuth()`(React 系 SDK)或 composables(Vue/Nuxt);都返回相同 `orgId`/`orgSlug`/`orgRole`。
  21. 动态路由按 org slug:使用 `app/orgs/[slug]/page.tsx`、`app/orgs/[slug]/settings/page.tsx`;始终验证 URL slug 等于活跃 org slug,否则重定向到无权限流程,防止用户用 stale `orgSlug` 访问 `/orgs/other-org/...`。
  22. RBAC:`const { has } = await auth()`;角色检查 `has({ role: 'org:admin' })`;权限检查 `has({ permission: 'org:sys_memberships:manage' })`。System Permissions 前缀 `org:sys_`;自定义权限用 `org:<resource>:<action>`。短列表:`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`。
  23. 条件渲染:Core 3 用 `<Show when={{ role: 'org:admin' }}>` 或 `<Show when={{ permission: 'org:sys_memberships:manage' }}>`;Core 2 用 `<Protect role="org:admin">` 或 `<Protect permission="...">`。Astro 模板从 `@clerk/astro/components` 导入同样组件。
  24. OrganizationSwitcher:`<OrganizationSwitcher hidePersonal afterCreateOrganizationUrl="/orgs/:slug/dashboard" afterSelectOrganizationUrl="/orgs/:slug/dashboard" />`。关键 props:`hidePersonal`(B2B-only 传 `true`,默认 `false`)、`afterCreateOrganizationUrl`、`afterSelectOrganizationUrl`、`afterLeaveOrganizationUrl`、`afterSelectPersonalUrl`(`:slug` 运行时替换)、`createOrganizationMode`/`organizationProfileMode`(`'modal' | 'navigation'`,默认 `'modal'`)。
  25. Session Task `choose-organization`:当 `Membership required` 启用,无 org 用户在登录后自动进入该 task;`<SignIn />` 自动处理。自行托管时用 `<ClerkProvider taskUrls={{ 'choose-organization': '/session-tasks/choose-organization' }}>`,并创建 `app/session-tasks/choose-organization/page.tsx` 返回 `<TaskChooseOrganization redirectUrlComplete="/dashboard" />`。React-based SDK 内置 `TaskChooseOrganization`;JS Frontend SDK 用 `clerk.mountTaskChooseOrganization(node)` / `clerk.unmountTaskChooseOrganization(node)`。Core 2 无 session tasks,需重定向到渲染 `<OrganizationSwitcher hidePersonal />` 的页面。
  26. 默认角色与权限:`org:admin` 拥有全部 System Permissions,可管理 org 和 memberships;`org:member` 只有读 members 和读 billing 权限。每个实例最多在 Dashboard → Organizations → Roles & Permissions 创建 10 个自定义角色;role-per-org 由 Role Sets 控制,完整模型见 `references/roles-permissions.md`。
  27. Billing 检查:启用 Clerk Billing 后,`has({ plan: 'gold' })` 检查订阅 plan,`has({ feature: 'widgets' })` 检查功能 entitlement;Core 2 的 `has()` 仅支持 `role` 和 `permission`。完整 Billing 与 seat-limit plan 模型见 `clerk-billing`。
  28. Enterprise SSO:按 org 配置 SAML/OIDC,在 Dashboard → Configure → Enterprise Connections,或 Organizations → 选择 org → SSO Connections。SSO connection 直接拥有域,不需要单独 Verified Domain,且两者在同一域上互斥。首次 SSO 登录自动加入用 JIT Provisioning,不是 Verified Domains。`provider` 字段在 `enterpriseConnection`,不在 `enterpriseAccounts[0]`。Core 3 strategy 为 `'enterprise_sso'`;Core 2 用 `'saml'` 和 `user.samlAccounts`。完整流程见 `references/enterprise-sso.md`。
  29. 限制 seats:创建 org 时传 `maxAllowedMemberships`,如 `clerk.organizations.createOrganization({ name: 'Acme Corp', createdBy: userId, maxAllowedMemberships: 10 })`;之后用 `clerk.organizations.updateOrganization(orgId, { maxAllowedMemberships: 25 })` 更新。按订阅层级限制 seats 时,使用 seat-limited Billing Plan(见 `clerk-billing`)。
  30. Billing 会在 Feature 层门禁权限:启用 Clerk Billing 后,若 `has({ permission: 'org:posts:edit' })` 对应 Feature 不在 org 活跃 Plan 中,即使角色已分配该 permission 也返回 `false`;确保在 Dashboard → Billing → Plans → Features 把 Feature 附加到活跃 Plan。
  31. Metadata 更新是 REPLACE 不是 merge:`updateOrganization({ publicMetadata })` 会覆盖全部 public metadata;先读 org,展开旧 metadata 再写,如 `publicMetadata: { ...org.publicMetadata, newField: 'value' }`;`privateMetadata` 和通过 `clerkClient.users.updateUser` 更新 user metadata 同理。
  32. 错误签名诊断时先查配置,不要先改组件:`orgId`/`orgSlug` 对已登录用户为 `undefined` → Organizations 未启用或用户无活跃 org;在 Dashboard → Organizations 启用、检查 Membership mode、展示 `<OrganizationSwitcher />`。`has({ permission: 'org:manage_members' })` 总为 `false` → 用了虚构 slug;改用 `org:sys_memberships:manage`。`has({ role })` 为 `false` 但用户像 admin → 角色变更后 session token stale;重新登录或 `await clerk.session?.reload()`。`has({ permission })` 为 `false` 即使角色已分配 → Feature 未附加到活跃 Plan;在 Dashboard → Billing → Plans 附加 Feature。`<OrganizationSwitcher />` 不显示 Personal Account → `Membership required` 开启(2025-08-22 起默认);改 Dashboard → Organizations settings → `Membership optional`。`TaskChooseOrganization` 抛 “cannot render when a user doesn't have current session tasks” → 在 `choose-organization` task 上下文外渲染;只在 `choose-organization` session-task route 中包裹,不要无条件渲染。`enterpriseAccounts[0].provider` 为 `undefined` → 嵌套层级错误;用 `user.enterpriseAccounts[0].enterpriseConnection?.provider`。
  33. 服务端保护 slug-scoped admin page 的完整授权示例:`const { orgSlug, has } = await auth()`;`if (orgSlug !== params.slug) redirect('/dashboard')`;`if (!has({ role: 'org:admin' })) redirect(`/orgs/${orgSlug}`)`;返回页面内容。中间件级保护见 `references/nextjs-patterns.md`。
  34. 从 server action 或 route handler 发送邀请短表:导入 `clerkClient, auth`;在 `inviteMember(organizationId, emailAddress, role)` 中检查 `userId` 存在、`has({ permission: 'org:sys_memberships:manage' })`;调用 `clerk.organizations.createOrganizationInvitation({ organizationId, inviterUserId: userId, emailAddress, role, redirectUrl: 'https://yourapp.com/accept-invite' })`。完整生命周期(list、revoke、bulk create、内置 `<OrganizationProfile />` UI)见 `references/invitations.md`。
  35. 工作流:1) 启用 Organizations + Membership mode;2) 通过 UI 组件或 Backend API 创建 org;3) 用 Backend API 或内置 UI 邀请成员,带 `inviterUserId`;4) 用 canonical `org:sys_*` 名称配合 `has({ role })`/`has({ permission })` 门禁;5) 每个受保护页面检查 `orgSlug === params.slug`;6) 用 `<OrganizationSwitcher />` 处理 org 切换。
  36. 相关技能:`clerk-setup` 初始 Clerk 安装;`clerk-billing` seat-limit plans、per-plan billing、`has({ plan })`/`has({ feature })`;`clerk-webhooks` 把 org events 同步到数据库(`organization.created`、`organizationMembership.*`);`clerk-backend-api` 完整 Backend API;`clerk-nextjs-patterns` 框架特定 middleware、server actions、caching。

适用边界 · 注意事项