组织
组织插件允许您管理组织的成员和团队。
组织简化了用户访问和权限管理。分配角色和权限以简化项目管理、团队协调和合作。
安装
将插件添加到您的 auth 配置中
import { betterAuth } from "better-auth"
import { organization } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
organization()
]
})添加客户端插件
import { createAuthClient } from "better-auth/client"
import { organizationClient } from "better-auth/client/plugins"
export const authClient = createAuthClient({
plugins: [
organizationClient()
]
})用法
安装插件后,您就可以开始使用组织插件来管理组织的成员和团队。客户端插件会在 organization 命名空间下提供方法,服务器端的 api 会提供管理组织所需的接口,并让您更轻松地调用后端函数。
组织
创建组织
const metadata = { someKey: "someValue" };const { data, error } = await authClient.organization.create({ name: "My Organization", // required slug: "my-org", // required logo: "https://example.com/logo.png", metadata, userId: "some_user_id", keepCurrentActiveOrganization: false,});namestringrequired组织名称。
slugstringrequired组织短名称(slug)。
logostring组织徽标。
metadataRecord<string, any>组织元数据。
userIdstring组织创建者的用户 ID。 @serverOnly - 如果提供会话头,则忽略此字段。
keepCurrentActiveOrganizationboolean新建组织后是否保持当前活跃组织。
互斥参数
userId 和会话头不能同时使用:
- 使用会话头时: 组织为认证会话用户创建。
userId字段将被静默忽略。 - 不使用会话头(仅服务器端): 组织为指定的
userId创建。
管理员说明: 若需代表其他用户创建组织,必须在服务器端调用 API,且不传递会话头。
限制谁可以创建组织
默认情况下,任何用户都可以创建组织。若要限制,设置 allowUserToCreateOrganization 选项为返回布尔值的函数,或直接为 true 或 false。
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
const auth = betterAuth({
//...
plugins: [
organization({
allowUserToCreateOrganization: async (user) => {
const subscription = await getSubscription(user.id);
return subscription.plan === "pro";
},
}),
],
});检查组织短名称是否已被占用
您可以使用客户端提供的 checkSlug 函数检查组织短名称是否已被占用。此函数接受一个包含以下属性的对象:
const { data, error } = await authClient.organization.checkSlug({ slug: "my-org", // required});slugstringrequired要检查的组织短名称。
组织钩子
您可以使用钩子来自定义组织操作,钩子会在各种组织相关活动的前后运行。Better Auth 提供了两种配置钩子方式:
- 遗留的 organizationCreation 钩子(已弃用,改用
organizationHooks) - 现代的 organizationHooks(推荐)- 提供对所有组织活动的全面控制
组织创建与管理钩子
控制组织生命周期操作:
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
organizationHooks: {
// 组织创建前钩子
beforeCreateOrganization: async ({ organization, user }) => {
// 创建组织前运行自定义逻辑
// 可选地修改组织数据
return {
data: {
...organization,
metadata: {
customField: "value",
},
},
};
},
afterCreateOrganization: async ({ organization, member, user }) => {
// 创建组织后运行自定义逻辑
// 例如,创建默认资源,发送通知
await setupDefaultResources(organization.id);
},
// 组织更新前钩子
beforeUpdateOrganization: async ({ organization, user, member }) => {
// 验证更新,应用业务规则
return {
data: {
...organization,
name: organization.name?.toLowerCase(),
},
};
},
afterUpdateOrganization: async ({ organization, user, member }) => {
// 同步更改到外部系统
await syncOrganizationToExternalSystems(organization);
},
},
}),
],
});遗留的 organizationCreation 钩子仍受支持但已弃用。
新项目请使用 organizationHooks.beforeCreateOrganization 和
organizationHooks.afterCreateOrganization。
成员钩子
控制组织内成员操作:
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
organizationHooks: {
// 添加成员前
beforeAddMember: async ({ member, user, organization }) => {
// 自定义验证或修改
console.log(`Adding ${user.email} to ${organization.name}`);
// 可选地修改成员数据
return {
data: {
...member,
role: "custom-role", // 覆盖角色
},
};
},
// 添加成员后
afterAddMember: async ({ member, user, organization }) => {
// 发送欢迎邮件,创建默认资源等
await sendWelcomeEmail(user.email, organization.name);
},
// 移除成员前
beforeRemoveMember: async ({ member, user, organization }) => {
// 清理用户资源,发送通知等
await cleanupUserResources(user.id, organization.id);
},
// 移除成员后
afterRemoveMember: async ({ member, user, organization }) => {
await logMemberRemoval(user.id, organization.id);
},
// 更新成员角色前
beforeUpdateMemberRole: async ({
member,
newRole,
user,
organization,
}) => {
// 验证角色变更权限
if (newRole === "owner" && !hasOwnerUpgradePermission(user)) {
throw new Error("Cannot upgrade to owner role");
}
// 可选地修改角色
return {
data: {
role: newRole,
},
};
},
// 更新成员角色后
afterUpdateMemberRole: async ({
member,
previousRole,
user,
organization,
}) => {
await logRoleChange(user.id, previousRole, member.role);
},
},
}),
],
});邀请钩子
控制邀请流程:
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
organizationHooks: {
// 创建邀请前
beforeCreateInvitation: async ({
invitation,
inviter,
organization,
}) => {
// 自定义验证或过期逻辑
const customExpiration = new Date(
Date.now() + 1000 * 60 * 60 * 24 * 7
); // 7 天
return {
data: {
...invitation,
expiresAt: customExpiration,
},
};
},
// 创建邀请后
afterCreateInvitation: async ({
invitation,
inviter,
organization,
}) => {
// 发送自定义邀请邮件,统计指标等
await sendCustomInvitationEmail(invitation, organization);
},
// 接受邀请前
beforeAcceptInvitation: async ({ invitation, user, organization }) => {
// 接受前额外验证
await validateUserEligibility(user, organization);
},
// 接受邀请后
afterAcceptInvitation: async ({
invitation,
member,
user,
organization,
}) => {
// 设置用户账户,分配默认资源
await setupNewMemberResources(user, organization);
},
// 拒绝邀请前后
beforeRejectInvitation: async ({ invitation, user, organization }) => {
// 记录拒绝原因,通知邀请者等
},
afterRejectInvitation: async ({ invitation, user, organization }) => {
await notifyInviterOfRejection(invitation.inviterId, user.email);
},
// 取消邀请前后
beforeCancelInvitation: async ({
invitation,
cancelledBy,
organization,
}) => {
// 验证取消权限
},
afterCancelInvitation: async ({
invitation,
cancelledBy,
organization,
}) => {
await logInvitationCancellation(invitation.id, cancelledBy.id);
},
},
}),
],
});团队钩子
控制团队操作(启用团队时生效):
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
teams: { enabled: true },
organizationHooks: {
// 创建团队前
beforeCreateTeam: async ({ team, user, organization }) => {
// 验证团队名称,应用命名规范
return {
data: {
...team,
name: team.name.toLowerCase().replace(/\s+/g, "-"),
},
};
},
// 创建团队后
afterCreateTeam: async ({ team, user, organization }) => {
// 创建默认团队资源、频道等
await createDefaultTeamResources(team.id);
},
// 更新团队前
beforeUpdateTeam: async ({ team, updates, user, organization }) => {
// 验证更新,应用业务规则
return {
data: {
...updates,
name: updates.name?.toLowerCase(),
},
};
},
// 更新团队后
afterUpdateTeam: async ({ team, user, organization }) => {
await syncTeamChangesToExternalSystems(team);
},
// 删除团队前
beforeDeleteTeam: async ({ team, user, organization }) => {
// 备份团队数据,通知成员
await backupTeamData(team.id);
},
// 删除团队后
afterDeleteTeam: async ({ team, user, organization }) => {
await cleanupTeamResources(team.id);
},
// 团队成员操作
beforeAddTeamMember: async ({
teamMember,
team,
user,
organization,
}) => {
// 验证团队成员限制,权限
const memberCount = await getTeamMemberCount(team.id);
if (memberCount >= 10) {
throw new Error("Team is full");
}
},
afterAddTeamMember: async ({
teamMember,
team,
user,
organization,
}) => {
await grantTeamAccess(user.id, team.id);
},
beforeRemoveTeamMember: async ({
teamMember,
team,
user,
organization,
}) => {
// 备份用户的团队专属数据
await backupTeamMemberData(user.id, team.id);
},
afterRemoveTeamMember: async ({
teamMember,
team,
user,
organization,
}) => {
await revokeTeamAccess(user.id, team.id);
},
},
}),
],
});钩子错误处理
所有钩子支持错误处理。在 before 钩子中抛出错误将阻止操作执行:
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { APIError } from "better-auth/api";
export const auth = betterAuth({
plugins: [
organization({
organizationHooks: {
beforeAddMember: async ({ member, user, organization }) => {
// 检查用户是否存在未处理违规
const violations = await checkUserViolations(user.id);
if (violations.length > 0) {
throw new APIError("BAD_REQUEST", {
message:
"User has pending violations and cannot join organizations",
});
}
},
beforeCreateTeam: async ({ team, user, organization }) => {
// 验证团队名称唯一性
const existingTeam = await findTeamByName(team.name, organization.id);
if (existingTeam) {
throw new APIError("BAD_REQUEST", {
message: "Team name already exists in this organization",
});
}
},
},
}),
],
});列出用户所在组织
要列出用户所在的所有组织,可以使用 useListOrganizations 钩子。它实现了响应式地获取用户成员组织列表的功能。
import { authClient } from "@/lib/auth-client"
function App(){
const { data: organizations } = authClient.useListOrganizations()
return (
<div>
{organizations.map((org) => (
<p>{org.name}</p>
))}
</div>)
}<script lang="ts">
import { authClient } from "$lib/auth-client";
const organizations = authClient.useListOrganizations();
</script>
<h1>组织</h1>
{#if $organizations.isPending}
<p>加载中...</p>
{:else if !$organizations.data?.length}
<p>未找到组织。</p>
{:else}
<ul>
{#each $organizations.data as organization}
<li>{organization.name}</li>
{/each}
</ul>
{/if}<script lang="ts">;
export default {
setup() {
const organizations = authClient.useListOrganizations()
return { organizations };
}
};
</script>
<template>
<div>
<h1>组织</h1>
<div v-if="organizations.isPending">加载中...</div>
<div v-else-if="organizations.data === null">未找到组织。</div>
<ul v-else>
<li v-for="organization in organizations.data" :key="organization.id">
{{ organization.name }}
</li>
</ul>
</div>
</template>或者,如果不想使用钩子,也可以调用 organization.list。
const { data, error } = await authClient.organization.list();活跃组织
活跃组织是用户当前工作的工作区。默认情况下,用户登录时活跃组织设置为 null。您可以为用户会话设置活跃组织。
并非所有情况下都需要在会话中持久化活跃组织。 您可以仅在客户端管理活跃组织。例如, 多个标签页可以有不同的活跃组织。
设置活跃组织
您可以调用 organization.setActive 函数设置活跃组织。它会将活跃组织设置到用户会话中。
在某些应用中,您可能希望能取消设置活跃组织。
这时可以调用此接口,将 organizationId 设置为 null。
const { data, error } = await authClient.organization.setActive({ organizationId: "org-id", organizationSlug: "org-slug",});organizationIdstring | null要设置的活跃组织 ID。可以为 null 以取消活跃组织。
organizationSlugstring要设置的活跃组织短名称。如果未传递 organizationId,也可设置为 null 取消活跃组织。
要在会话创建时自动设置活跃组织,可以使用 数据库钩子。您需要实现逻辑,以确定初始活跃组织。
export const auth = betterAuth({
import { betterAuth } from "better-auth";
databaseHooks: {
session: {
create: {
before: async (session) => {
// 自定义设置初始活跃组织逻辑
const organization = await getInitialOrganization(session.userId);
return {
data: {
...session,
activeOrganizationId: organization?.id,
},
};
},
},
},
},
});使用活跃组织
要获取用户的活跃组织,可以调用 useActiveOrganization 钩子。该钩子返回用户的活跃组织并会在其变化时重新评估。
import { authClient } from "@/lib/auth-client"
function App(){
const { data: activeOrganization } = authClient.useActiveOrganization()
return (
<div>
{activeOrganization ? <p>{activeOrganization.name}</p> : null}
</div>
)
}<script lang="ts">
import { authClient } from "$lib/auth-client";
const activeOrganization = authClient.useActiveOrganization();
</script>
<h2>活跃组织</h2>
{#if $activeOrganization.isPending}
<p>加载中...</p>
{:else if $activeOrganization.data === null}
<p>未找到活跃组织。</p>
{:else}
<p>{$activeOrganization.data.name}</p>
{/if}<script lang="ts">;
export default {
setup() {
const activeOrganization = authClient.useActiveOrganization();
return { activeOrganization };
}
};
</script>
<template>
<div>
<h2>活跃组织</h2>
<div v-if="activeOrganization.isPending">加载中...</div>
<div v-else-if="activeOrganization.data === null">无活跃组织。</div>
<div v-else>
{{ activeOrganization.data.name }}
</div>
</div>
</template>获取完整组织信息
要获取组织的完整详情,可以使用 getFullOrganization 函数。默认情况下,如果不传任何属性,将使用活跃组织。
const { data, error } = await authClient.organization.getFullOrganization({ query: { organizationId: "org-id", organizationSlug: "org-slug", membersLimit: 100, },});organizationIdstring要获取的组织 ID。默认使用活跃组织。
organizationSlugstring要获取的组织短名称。
membersLimitnumber要获取的成员数量限制。默认使用 membershipLimit 选项,默认值为 100。
更新组织
要更新组织信息,可以使用 organization.update。
const { data, error } = await authClient.organization.update({ data: { // required name: "updated-name", slug: "updated-slug", logo: "new-logo.url", metadata: { customerId: "test" }, }, organizationId: "org-id",});dataObjectrequired要更新组织的部分数据。
namestring组织名称。
slugstring组织短名称。
logostring组织徽标。
metadataRecord<string, any> | null组织元数据。
organizationIdstring要更新的组织 ID。
删除组织
要删除用户拥有的组织,可以使用 organization.delete。
const { data, error } = await authClient.organization.delete({ organizationId: "org-id", // required});organizationIdstringrequired要删除的组织 ID。
如果用户在指定组织中拥有必要权限(默认角色为所有者),则会删除所有成员、邀请和组织信息。
您可以通过 organizationDeletion 选项配置组织删除的处理方式:
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
const auth = betterAuth({
plugins: [
organization({
disableOrganizationDeletion: true, // 完全禁用删除功能
organizationHooks: {
beforeDeleteOrganization: async (data, request) => {
// 删除组织前回调
},
afterDeleteOrganization: async (data, request) => {
// 删除组织后回调
},
},
}),
],
});邀请
要添加成员到组织,首先需要向用户发送邀请。用户会收到包含邀请链接的邮件或短信。用户接受邀请后,将被添加到组织中。
设置邀请邮件
要实现成员邀请功能,首先需要为 better-auth 实例配置 sendInvitationEmail。此函数负责向用户发送邀请邮件。
您需要构造并发送邀请链接给用户,该链接应包含邀请 ID,用户点击时用于调用 acceptInvitation。
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { sendOrganizationInvitation } from "./email";
export const auth = betterAuth({
plugins: [
organization({
async sendInvitationEmail(data) {
const inviteLink = `https://example.com/accept-invitation/${data.id}`;
sendOrganizationInvitation({
email: data.email,
invitedByUsername: data.inviter.user.name,
invitedByEmail: data.inviter.user.email,
teamName: data.organization.name,
inviteLink,
});
},
}),
],
});发送邀请
要邀请用户加入组织,可以使用客户端提供的 invite 函数。此函数接受如下对象:
const { data, error } = await authClient.organization.inviteMember({ email: "example@gmail.com", // required role: "member", // required organizationId: "org-id", resend: true, teamId: "team-id",});emailstringrequired受邀用户的邮箱地址。
rolestring | string[]required指定赋予用户的角色,支持 admin、member、owner。
organizationIdstring要邀请用户加入的组织 ID,默认活跃组织。
resendboolean若用户已被邀请,是否重发邀请邮件。
teamIdstring要邀请用户加入的团队 ID。
- 若用户已是组织成员,邀请将被取消。- 若用户已被邀请(非重发),邀请邮件不会重复发送。- 若设置了
cancelPendingInvitationsOnReInvite为true,则会取消已存在邀请并发送新邀请。
接受邀请
用户收到邀请邮件后,可点击邀请链接接受邀请。邀请链接应包含邀请 ID,接受时用于调用对应接口。
请确保用户登录后调用 acceptInvitation。
const { data, error } = await authClient.organization.acceptInvitation({ invitationId: "invitation-id", // required});invitationIdstringrequired要接受的邀请 ID。
邮箱验证要求
如果组织配置中启用了 requireEmailVerificationOnInvitation,则用户必须验证邮箱后才能接受邀请,确保只有验证过邮箱的用户能加入组织。
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
requireEmailVerificationOnInvitation: true,
async sendInvitationEmail(data) {
// 邮件发送逻辑
},
}),
],
});取消邀请
若发出邀请后需要取消,可以调用此方法。
若用户想要拒绝邀请,详见拒绝邀请。
await authClient.organization.cancelInvitation({ invitationId: "invitation-id", // required});invitationIdstringrequired要取消的邀请 ID。
拒绝邀请
用户收到邀请但想拒绝,可调用此方法。
await authClient.organization.rejectInvitation({ invitationId: "invitation-id", // required});invitationIdstringrequired要拒绝的邀请 ID。
与接受邀请类似,启用 requireEmailVerificationOnInvitation 后,拒绝邀请也需要邮箱验证。未验证邮箱的用户拒绝时会报错。
获取邀请
您可以使用客户端的 organization.getInvitation 获取邀请信息。需提供邀请 ID。
const { data, error } = await authClient.organization.getInvitation({ query: { id: "invitation-id", // required },});idstringrequired要获取的邀请 ID。
列出邀请
要列出特定组织的所有邀请,可使用客户端的 listInvitations。
const { data, error } = await authClient.organization.listInvitations({ query: { organizationId: "organization-id", },});organizationIdstring可选组织 ID,默认活跃组织。
列出用户邀请
要列出给特定用户的所有邀请,可使用客户端的 listUserInvitations。
import { authClient } from "@/lib/auth-client"
const invitations = await authClient.organization.listUserInvitations();服务器端可通过查询参数传递用户邮箱。
const invitations = await auth.api.listUserInvitations({
query: {
email: "user@example.com",
},
});查询参数 email 仅在服务器端可用,用于查询指定用户的邀请。
成员
列出成员
您可以使用 listMembers 函数列出组织的所有成员。
const { data, error } = await authClient.organization.listMembers({ query: { organizationId: "organization-id", limit: 100, offset: 0, sortBy: "createdAt", sortDirection: "desc", filterField: "createdAt", filterOperator: "eq", filterValue: "value", },});organizationIdstring可选的组织 ID,默认活跃组织。
limitnumber返回成员数量限制。
offsetnumber起始偏移量。
sortBystring排序字段。
sortDirection"asc" | "desc"排序方向。
filterFieldstring过滤字段。
filterOperator"eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with"过滤操作符。
filterValuestring | number | boolean | string[] | number[]过滤值。
移除成员
您可以调用 organization.removeMember 来移除成员。
const { data, error } = await authClient.organization.removeMember({ memberIdOrEmail: "user@example.com", // required organizationId: "org-id",});memberIdOrEmailstringrequired成员的 ID 或邮箱。
organizationIdstring要移除成员的组织 ID,默认活跃组织。
更新成员角色
您可以使用 organization.updateMemberRole 来更新组织内成员的角色。若当前用户有权限,角色将被更新。
await authClient.organization.updateMemberRole({ role: ["admin", "sale"], // required memberId: "member-id", // required organizationId: "organization-id",});rolestring | string[]required新角色,支持字符串或字符串数组。
memberIdstringrequired要更新的成员 ID。
organizationIdstring可选组织 ID。若不传,则使用会话头中的活跃组织。
获取当前活跃成员
获取当前活跃组织的成员详细信息,调用 organization.getActiveMember。
const { data: member, error } = await authClient.organization.getActiveMember();获取当前活跃成员角色
获取当前活跃组织的成员角色,调用 organization.getActiveMemberRole。
const { data: { role }, error } = await authClient.organization.getActiveMemberRole();添加成员
如果想直接添加成员到组织(不发送邀请),可以使用只能在服务器调用的 addMember 接口。
const data = await auth.api.addMember({ body: { userId: "user-id", role: ["admin", "sale"], // required organizationId: "org-id", teamId: "team-id", },});userIdstring | null要添加的用户 ID。如果为 null,则需传递会话头。
rolestring | string[]required指定赋予成员的角色。
organizationIdstring可选组织 ID。默认使用活跃组织。
teamIdstring可选团队 ID。
退出组织
调用 organization.leave 函数,让当前用户退出组织。
await authClient.organization.leave({ organizationId: "organization-id", // required});organizationIdstringrequired要退出的组织 ID。
访问控制
组织插件提供了灵活的访问控制系统。您可以根据用户在组织中的角色来控制其访问权限。您可以基于角色定义自己的权限集。
角色
默认情况下,组织中有三种角色:
owner(所有者):默认创建组织的用户,拥有组织的完全控制权,并可执行任何操作。
admin(管理员):拥有除删除组织和更改所有者外的完全控制权。
member(成员):权限受限,仅能读取组织数据,无权限创建、更新或删除资源。
用户可拥有多个角色。多个角色以逗号(",")分隔的字符串存储。
权限
默认定义了三个资源,每个资源具有两到三个操作。
organization:
update delete
member:
create update delete
invitation:
create cancel
所有者拥有所有资源和操作的完整控制。管理员拥有除删除组织和更改所有者权之外的完整控制。成员仅有读取权限。
自定义权限
插件提供了简单的方式,为每个角色定义自定义权限。
创建访问控制
先调用 createAccessControl 函数并传入声明对象。声明对象以资源名作为key,动作数组作为值。
import { createAccessControl } from "better-auth/plugins/access";
/**
* 请使用 `as const` 以便 TypeScript 正确推断类型。
*/
const statement = {
project: ["create", "share", "update", "delete"],
} as const;
const ac = createAccessControl(statement); 为减小包体积,请从 better-auth/plugins/access 导入,而非 better-auth/plugins。
创建角色
创建访问控制后,可以根据权限声明创建角色。
import { createAccessControl } from "better-auth/plugins/access";
const statement = {
project: ["create", "share", "update", "delete"],
} as const;
const ac = createAccessControl(statement);
const member = ac.newRole({
project: ["create"],
});
const admin = ac.newRole({
project: ["create", "update"],
});
const owner = ac.newRole({
project: ["create", "update", "delete"],
});
const myCustomRole = ac.newRole({
project: ["create", "update", "delete"],
organization: ["update"],
}); 创建自定义角色时会覆盖预设权限。若想保留原有权限,需要引入 defaultStatements,并将其和新声明、角色权限合并。
import { createAccessControl } from "better-auth/plugins/access";
import { defaultStatements, adminAc } from 'better-auth/plugins/organization/access'
const statement = {
...defaultStatements,
project: ["create", "share", "update", "delete"],
} as const;
const ac = createAccessControl(statement);
const admin = ac.newRole({
project: ["create", "update"],
...adminAc.statements,
});将角色传递给插件
创建角色后,将它们同时传给服务端和客户端的组织插件。
import { betterAuth } from "better-auth"
import { organization } from "better-auth/plugins"
import { ac, owner, admin, member } from "@/auth/permissions"
export const auth = betterAuth({
plugins: [
organization({
ac,
roles: {
owner,
admin,
member,
myCustomRole
}
}),
],
});同时也需传递访问控制和角色给客户端插件。
import { createAuthClient } from "better-auth/client"
import { organizationClient } from "better-auth/client/plugins"
import { ac, owner, admin, member, myCustomRole } from "@/auth/permissions"
export const authClient = createAuthClient({
plugins: [
organizationClient({
ac,
roles: {
owner,
admin,
member,
myCustomRole
}
})
]
})访问控制使用
权限检查:
可调用 api 提供的 hasPermission 方法检测用户权限。
import { auth } from "@/lib/auth"
await auth.api.hasPermission({
headers: await headers(),
body: {
permissions: {
project: ["create"], // 需与访问控制结构一致
},
},
});
// 可同时检查多个资源权限
await auth.api.hasPermission({
headers: await headers(),
body: {
permissions: {
project: ["create"],
sale: ["create"],
},
},
});若需要在客户端从服务器检查权限,可使用客户端的 hasPermission。
const canCreateProject = await authClient.organization.hasPermission({
permissions: {
project: ["create"],
},
});
// 同时检查多个权限
const canCreateProjectAndCreateSale =
await authClient.organization.hasPermission({
permissions: {
project: ["create"],
sale: ["create"],
},
});检查角色权限:
定义好角色权限后,客户端可用 checkRolePermission 方法同步检查权限,避免每次请求服务器。
import { authClient } from "@/lib/auth-client"
const canCreateProject = authClient.organization.checkRolePermission({
permissions: {
organization: ["delete"],
},
role: "admin",
});
// 同时检查多个权限
const canCreateProjectAndCreateSale =
authClient.organization.checkRolePermission({
permissions: {
organization: ["delete"],
member: ["delete"],
},
role: "admin",
});该方法不会包含任何动态角色,因其是客户端同步执行。 如需包含动态角色权限,请使用 hasPermission API。
动态访问控制
动态访问控制允许您在运行时为组织创建角色。通过在数据库表中存储组织关联的角色及权限实现。
启用动态访问控制
要启用动态访问控制,请向服务器和客户端插件传入 dynamicAccessControl 配置项,设置 enabled: true。
确保服务器端的 ac 实例已预定义,因其用于推断可用权限。
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { ac } from "@/auth/permissions";
export const auth = betterAuth({
plugins: [
organization({
ac, // 必须定义以支持动态访问控制
dynamicAccessControl: {
enabled: true,
},
})
]
})import { createAuthClient } from "better-auth/client";
import { organizationClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [
organizationClient({
dynamicAccessControl: {
enabled: true,
},
})
]
})启用后,需要迁移新增数据库中 organizationRole 表。
authClient.organization.checkRolePermission 函数仍不会包含动态角色,因其是客户端同步执行。
请使用 hasPermission API 以包含动态角色权限检测。
创建角色
运行时为组织创建新角色,可调用 createRole。
仅具备 ac 资源中 create 权限的角色可创建新角色。
默认只有管理员和所有者拥有该权限。
且无法添加超出自身权限的权限。
// 使用自定义资源或权限时,确保在组织的 `ac` 配置中定义。const permission = { project: ["create", "update", "delete"]}await authClient.organization.createRole({ role: "my-unique-role", // required permission: permission, organizationId: "organization-id",});rolestringrequired角色名,需唯一。
permissionRecord<string, string[]>赋予角色的权限。
organizationIdstring创建角色的组织 ID,默认活跃组织。
之后可随意调用 updateMemberRole,将新角色赋予成员!
删除角色
删除角色时调用 deleteRole,需提供 roleName 或 roleId 以及 organizationId。
await authClient.organization.deleteRole({ roleName: "my-role", roleId: "role-id", organizationId: "organization-id",});roleNamestring角色名,或使用 roleId 替代。
roleIdstring角色 ID,或使用 roleName 替代。
organizationIdstring删除角色的组织 ID,默认活跃组织。
列出角色
调用 listOrgRoles 列出组织内的所有角色。
该操作需要成员具有 ac 资源中的 read 权限。
const { data: roles, error } = await authClient.organization.listRoles({ query: { organizationId: "organization-id", },});organizationIdstring组织 ID,默认活跃组织。
获取指定角色
调用 getOrgRole 获取特定角色,需传入 roleName 或 roleId。
该操作需要成员具有 ac 资源中的 read 权限。
const { data: role, error } = await authClient.organization.getRole({ query: { roleName: "my-role", roleId: "role-id", organizationId: "organization-id", },});roleNamestring角色名,或使用 roleId 替代。
roleIdstring角色 ID,或使用 roleName 替代。
organizationIdstring组织 ID,默认活跃组织。
更新角色
调用 updateOrgRole 更新角色,需传入 roleName 或 roleId。
const { data: updatedRole, error } = await authClient.organization.updateRole({ roleName: "my-role", roleId: "role-id", organizationId: "organization-id", data: { // required permission: { project: ["create", "update", "delete"] }, roleName: "my-new-role", },});roleNamestring角色名,或使用 roleId 替代。
roleIdstring角色 ID,或使用 roleName 替代。
organizationIdstring组织 ID,默认活跃组织。
dataObjectrequired待更新数据
permissionRecord<string, string[]>可选更新角色权限。
roleNamestring可选更新角色名。
配置选项
以下是可传递给 dynamicAccessControl 对象的选项列表。
enabled
启用或禁用动态访问控制,默认禁用。
organization({
dynamicAccessControl: {
enabled: true
}
})maximumRolesPerOrganization
限制每个组织可创建角色数量。
默认无限制。
organization({
dynamicAccessControl: {
maximumRolesPerOrganization: 10
}
})也可以传入返回数字的函数。
organization({
dynamicAccessControl: {
maximumRolesPerOrganization: async (organizationId) => {
const organization = await getOrganization(organizationId);
return organization.plan === "pro" ? 100 : 10;
}
}
})额外字段
要在 organizationRole 表中添加额外字段,可传递 additionalFields 配置项。
organization({
schema: {
organizationRole: {
additionalFields: {
// 角色颜色!
color: {
type: "string",
defaultValue: "#ffffff",
},
//... 其他字段
},
},
},
})若未使用 inferOrgAdditionalFields 推断额外字段,可以使用它推断。
import { createAuthClient } from "better-auth/client"
import { organizationClient, inferOrgAdditionalFields } from "better-auth/client/plugins"
import type { auth } from "@/lib/auth" // 仅导入 auth 对象类型
export const authClient = createAuthClient({
plugins: [
organizationClient({
schema: inferOrgAdditionalFields<typeof auth>()
})
]
})否则,可以直接传递 schema 值,和服务器端插件相同。
import { createAuthClient } from "better-auth/client"
import { organizationClient } from "better-auth/client/plugins"
export const authClient = createAuthClient({
plugins: [
organizationClient({
schema: {
organizationRole: {
additionalFields: {
color: {
type: "string",
defaultValue: "#ffffff",
}
}
}
}
})
]
})团队
团队允许您在组织内对成员进行分组。团队功能提供了额外的组织结构,可用于更细粒度地管理权限。
启用团队
传递 teams 配置项给服务器和客户端插件启用团队:
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
teams: {
enabled: true,
maximumTeams: 10, // 可选:限制团队数量
allowRemovingAllTeams: false, // 可选:禁止移除最后一个团队
},
}),
],
});import { createAuthClient } from "better-auth/client";
import { organizationClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [
organizationClient({
teams: {
enabled: true,
},
}),
],
});团队管理
创建团队
在组织内创建新团队:
const { data, error } = await authClient.organization.createTeam({ name: "my-team", // required organizationId: "organization-id",});namestringrequired团队名称。
organizationIdstring团队所属组织 ID,默认活跃组织。
列出团队
获取组织内所有团队:
const { data, error } = await authClient.organization.listTeams({ query: { organizationId: "organization-id", },});organizationIdstring组织 ID,默认活跃组织。
更新团队
更新团队详情:
const { data, error } = await authClient.organization.updateTeam({ teamId: "team-id", // required data: { // required name: "My new team name", organizationId: "My new organization ID for this team", createdAt: new Date(), updatedAt: new Date(), },});teamIdstringrequired要更新的团队 ID。
dataObjectrequired部分更新数据。
namestring团队名称。
organizationIdstring归属组织 ID。
createdAtDate创建时间。
updatedAtDate更新时间。
移除团队
从组织删除团队:
const { data, error } = await authClient.organization.removeTeam({ teamId: "team-id", // required organizationId: "organization-id",});teamIdstringrequired要移除的团队 ID。
organizationIdstring所属组织 ID,默认活跃组织。
设置活跃团队
设置当前活跃团队;teamId 为 null 时取消活跃团队。
const { data, error } = await authClient.organization.setActiveTeam({ teamId: "team-id",});teamIdstring要设置为活跃的团队 ID。
列出用户团队
列出当前用户所属的所有团队。
const { data, error } = await authClient.organization.listUserTeams();列出团队成员
列出指定团队的成员。
const { data, error } = await authClient.organization.listTeamMembers({ query: { teamId: "team-id", },});teamIdstring团队 ID。不传时返回活跃团队成员。
添加团队成员
添加成员到团队。
const { data, error } = await authClient.organization.addTeamMember({ teamId: "team-id", // required userId: "user-id", // required});teamIdstringrequired所属团队。
userIdstringrequired用户 ID。
移除团队成员
从团队移除成员。
const { data, error } = await authClient.organization.removeTeamMember({ teamId: "team-id", // required userId: "user-id", // required});teamIdstringrequired所属团队。
userIdstringrequired用户 ID。
团队权限
团队使用组织权限体系。管理团队需要以下权限:
team:create- 创建团队team:update- 更新团队team:delete- 删除团队
默认情况下:
- 组织所有者和管理员可以管理团队
- 普通成员无管理权限
团队配置选项
团队功能支持以下配置:
-
maximumTeams: 限制每组织最大团队数teams: { enabled: true, maximumTeams: 10 // 固定数目 // OR maximumTeams: async ({ organizationId, session }, ctx) => { // 基于组织计划动态限制 const plan = await getPlan(organizationId) return plan === 'pro' ? 20 : 5 }, maximumMembersPerTeam: 10 // 固定数目 // OR maximumMembersPerTeam: async ({ teamId, session, organizationId }, ctx) => { // 基于团队计划动态限制 const plan = await getPlan(organizationId, teamId) return plan === 'pro' ? 50 : 10 }, } -
allowRemovingAllTeams: 是否允许移除最后一个团队teams: { enabled: true, allowRemovingAllTeams: false // 禁止移除最后一个团队 }
团队成员
邀请成员加入组织时可指定团队:
await authClient.organization.inviteMember({
email: "user@example.com",
role: "member",
teamId: "team-id",
});用户接受邀请后会加入指定团队。
数据库模式
启用团队时,数据库中新增 team 和 teamMember 表。
表名:team
表名:teamMember
模式
组织插件向数据库添加以下表:
组织
表名:organization
成员
表名:member
邀请
表名:invitation
启用团队时,需要在邀请表添加如下字段:
会话
表名:session
需向会话表中添加两个字段,用于存储活跃组织 ID 和活跃团队 ID。
组织角色(可选)
表名:organizationRole
团队(可选)
表名:team
表名:teamMember
表名:invitation
自定义模式
若需更改表名或字段,可传递 schema 选项给组织插件。
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
const auth = betterAuth({
plugins: [
organization({
schema: {
organization: {
modelName: "organizations", // 将 organization 表映射为 organizations
fields: {
name: "title", // 将 name 字段映射为 title
},
additionalFields: {
// 为组织表添加新字段
myCustomField: {
type: "string",
input: true,
required: false,
},
},
},
},
}),
],
});额外字段
从 Better Auth v1.3 起,您可轻松为 organization、invitation、member 和 team 表添加自定义字段。
添加额外字段后,相应 API 端点会自动接收和返回这些字段。比如在 organization 表中添加字段,createOrganization 接口会支持请求体和响应中包含该字段。
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
const auth = betterAuth({
plugins: [
organization({
schema: {
organization: {
additionalFields: {
myCustomField: {
type: "string",
input: true,
required: false,
},
},
},
},
}),
],
});若要推断额外字段,可使用 inferOrgAdditionalFields 函数。它会根据 auth 对象类型推断。
import { createAuthClient } from "better-auth/client";
import {
inferOrgAdditionalFields,
organizationClient,
} from "better-auth/client/plugins";
import type { auth } from "@/lib/auth" // 仅导入 auth 类型
const client = createAuthClient({
plugins: [
organizationClient({
schema: inferOrgAdditionalFields<typeof auth>(),
}),
],
});若无法导入 auth 类型,也可传入 schema 对象来推断。
import { createAuthClient } from "better-auth/client";
import {
inferOrgAdditionalFields,
organizationClient,
} from "better-auth/client/plugins";
const client = createAuthClient({
plugins: [
organizationClient({
schema: inferOrgAdditionalFields({
organization: {
additionalFields: {
newField: {
type: "string",
},
},
},
}),
}),
],
});示例用法
await client.organization.create({
name: "Test",
slug: "test",
newField: "123", // 允许
//@ts-expect-error - 不存在字段
unavailableField: "123", // 不允许
});选项
allowUserToCreateOrganization: boolean | ((user: User) => Promise<boolean> | boolean) - 决定用户是否可创建组织的函数。默认 true。设置 false 禁止创建。
organizationLimit: number | ((user: User) => Promise<boolean> | boolean) - 用户允许创建的最大组织数。默认无限制。传函数时,函数应返回 true 表示已达限制(禁止新建),返回 false 表示未达限制(允许新建)。
creatorRole: admin | owner - 创建组织的用户角色,默认 owner,可设为 admin。
membershipLimit: number | ((user: User, organization: Organization) => Promise<number> | number) - 组织内允许的最大成员数。默认 100。
sendInvitationEmail: async (data) => Promise<void> - 发送邀请邮件的异步函数。
invitationExpiresIn : number - 邀请链接有效时间(秒),默认 48 小时(2 天)。
cancelPendingInvitationsOnReInvite: boolean - 如果用户已邀请,是否取消待处理邀请。默认 false。
invitationLimit: number | ((user: User) => Promise<boolean> | boolean) - 用户允许的最大邀请数。默认 100。
requireEmailVerificationOnInvitation: boolean - 是否要求用户在接受或拒绝邀请前先验证邮箱。默认 false。开启后,未验证邮箱的用户会被拒绝操作邀请。