组织

组织简化了用户访问和权限管理。分配角色和权限以优化项目管理、团队协作和合作关系。

【Organizations simplifies user access and permissions management. Assign roles and permissions to streamline project management, team coordination, and partnerships.】

安装

【Installation】

将插件添加到你的 auth 配置中

【Add the plugin to your auth config】

auth.ts
import { betterAuth } from "better-auth"
import { organization } from "better-auth/plugins"

export const auth = betterAuth({
    plugins: [ 
        organization() 
    ] 
})

迁移数据库

运行迁移或生成架构,以向数据库添加必要的字段和表。

npx @better-auth/cli migrate
npx @better-auth/cli generate

See the Schema section to add the fields manually.

添加客户端插件

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { organizationClient } from "better-auth/client/plugins"

export const authClient = createAuthClient({
    plugins: [ 
        organizationClient() 
    ] 
})

用法

【Usage】

一旦安装了插件,你就可以开始使用组织插件来管理你的组织成员和团队。客户端插件将在 organization 命名空间下提供方法,而服务器端的 api 将为你提供管理组织所需的端点,并让你更轻松地在自己的后端调用这些函数。

【Once you've installed the plugin, you can start using the organization plugin to manage your organization's members and teams. The client plugin will provide you with methods under the organization namespace, and the server api will provide you with the necessary endpoints to manage your organization and give you an easier way to call the functions on your own backend.】

组织

【Organization】

创建一个组织

【Create an organization】

POST
/organization/create
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,});
PropDescriptionType
name
The organization name.
string
slug
The organization slug.
string
logo?
The organization logo.
string
metadata?
The metadata of the organization.
Record<string, any>
userId?
The user ID of the organization creator. @serverOnly - This is ignored if session headers are provided.
string
keepCurrentActiveOrganization?
Whether to keep the current active organization active after creating a new one.
boolean

互斥参数

userId 和会话头不能同时使用:

  • 使用会话头时: 组织会为经过身份验证的会话用户创建。userId 字段会被静默忽略
  • 不使用会话头时(仅服务器端): 组织会为 userId 指定的用户创建。

【The userId and session headers cannot be used together:

  • With session headers: The organization is created for the authenticated session user. The userId field is silently ignored.
  • Without session headers (Server-side only): The organization is created for the user specified by userId.】

管理员专用: 要代表其他用户创建组织,你必须在服务器端进行 API 调用,不要传递会话头信息。

限制谁可以创建组织

【Restrict who can create an organization】

默认情况下,任何用户都可以创建组织。要限制此操作,请将 allowUserToCreateOrganization 选项设置为返回布尔值的函数,或直接设置为 truefalse

【By default, any user can create an organization. To restrict this, set the allowUserToCreateOrganization option to a function that returns a boolean, or directly to true or false.】

auth.ts
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"; 
      }, 
    }),
  ],
});

检查组织缩略名是否已被使用

【Check if organization slug is taken】

要检查一个组织的标识名是否已被使用,你可以使用客户端提供的 checkSlug 函数。该函数接收一个具有以下属性的对象:

【To check if an organization slug is taken or not you can use the checkSlug function provided by the client. The function takes an object with the following properties:】

POST
/organization/check-slug
const { data, error } = await authClient.organization.checkSlug({    slug: "my-org", // required});
PropDescriptionType
slug
The organization slug to check.
string

组织钩子

【Organization Hooks】

你可以使用在各种与组织相关的活动之前和之后运行的钩子来自定义组织操作。Better Auth 提供了两种配置钩子的方法:

【You can customize organization operations using hooks that run before and after various organization-related activities. Better Auth provides two ways to configure hooks:】

  1. 旧版组织创建钩子(已弃用,请改用 organizationHooks
  2. 现代组织钩子(推荐)- 提供对所有与组织相关活动的全面控制

组织创建和管理钩子

【Organization Creation and Management Hooks】

控制组织生命周期操作:

【Control organization lifecycle operations:】

auth.ts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    organization({
      organizationHooks: {
        // Organization creation hooks
        beforeCreateOrganization: async ({ organization, user }) => {
          // Run custom logic before organization is created
          // Optionally modify the organization data
          return {
            data: {
              ...organization,
              metadata: {
                customField: "value",
              },
            },
          };
        },

        afterCreateOrganization: async ({ organization, member, user }) => {
          // Run custom logic after organization is created
          // e.g., create default resources, send notifications
          await setupDefaultResources(organization.id);
        },

        // Organization update hooks
        beforeUpdateOrganization: async ({ organization, user, member }) => {
          // Validate updates, apply business rules
          return {
            data: {
              ...organization,
              name: organization.name?.toLowerCase(),
            },
          };
        },

        afterUpdateOrganization: async ({ organization, user, member }) => {
          // Sync changes to external systems
          await syncOrganizationToExternalSystems(organization);
        },
      },
    }),
  ],
});

遗留的 organizationCreation 钩子仍然受支持,但已弃用。 对于新项目,请改用 organizationHooks.beforeCreateOrganizationorganizationHooks.afterCreateOrganization

成员钩子

【Member Hooks】

控制组织内的成员操作:

【Control member operations within organizations:】

auth.ts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    organization({
      organizationHooks: {
        // Before a member is added to an organization
        beforeAddMember: async ({ member, user, organization }) => {
          // Custom validation or modification
          console.log(`Adding ${user.email} to ${organization.name}`);

          // Optionally modify member data
          return {
            data: {
              ...member,
              role: "custom-role", // Override the role
            },
          };
        },

        // After a member is added
        afterAddMember: async ({ member, user, organization }) => {
          // Send welcome email, create default resources, etc.
          await sendWelcomeEmail(user.email, organization.name);
        },

        // Before a member is removed
        beforeRemoveMember: async ({ member, user, organization }) => {
          // Cleanup user's resources, send notification, etc.
          await cleanupUserResources(user.id, organization.id);
        },

        // After a member is removed
        afterRemoveMember: async ({ member, user, organization }) => {
          await logMemberRemoval(user.id, organization.id);
        },

        // Before updating a member's role
        beforeUpdateMemberRole: async ({
          member,
          newRole,
          user,
          organization,
        }) => {
          // Validate role change permissions
          if (newRole === "owner" && !hasOwnerUpgradePermission(user)) {
            throw new Error("Cannot upgrade to owner role");
          }

          // Optionally modify the role
          return {
            data: {
              role: newRole,
            },
          };
        },

        // After updating a member's role
        afterUpdateMemberRole: async ({
          member,
          previousRole,
          user,
          organization,
        }) => {
          await logRoleChange(user.id, previousRole, member.role);
        },
      },
    }),
  ],
});

邀请钩子

【Invitation Hooks】

控制邀请生命周期:

【Control invitation lifecycle:】

auth.ts
export const auth = betterAuth({
  plugins: [
    organization({
      organizationHooks: {
        // Before creating an invitation
        beforeCreateInvitation: async ({
          invitation,
          inviter,
          organization,
        }) => {
          // Custom validation or expiration logic
          const customExpiration = new Date(
            Date.now() + 1000 * 60 * 60 * 24 * 7
          ); // 7 days

          return {
            data: {
              ...invitation,
              expiresAt: customExpiration,
            },
          };
        },

        // After creating an invitation
        afterCreateInvitation: async ({
          invitation,
          inviter,
          organization,
        }) => {
          // Send custom invitation email, track metrics, etc.
          await sendCustomInvitationEmail(invitation, organization);
        },

        // Before accepting an invitation
        beforeAcceptInvitation: async ({ invitation, user, organization }) => {
          // Additional validation before acceptance
          await validateUserEligibility(user, organization);
        },

        // After accepting an invitation
        afterAcceptInvitation: async ({
          invitation,
          member,
          user,
          organization,
        }) => {
          // Setup user account, assign default resources
          await setupNewMemberResources(user, organization);
        },

        // Before/after rejecting invitations
        beforeRejectInvitation: async ({ invitation, user, organization }) => {
          // Log rejection reason, send notification to inviter
        },

        afterRejectInvitation: async ({ invitation, user, organization }) => {
          await notifyInviterOfRejection(invitation.inviterId, user.email);
        },

        // Before/after cancelling invitations
        beforeCancelInvitation: async ({
          invitation,
          cancelledBy,
          organization,
        }) => {
          // Verify cancellation permissions
        },

        afterCancelInvitation: async ({
          invitation,
          cancelledBy,
          organization,
        }) => {
          await logInvitationCancellation(invitation.id, cancelledBy.id);
        },
      },
    }),
  ],
});

队伍钩子

【Team Hooks】

控制团队操作(当启用团队时):

【Control team operations (when teams are enabled):】

auth.ts
export const auth = betterAuth({
  plugins: [
    organization({
      teams: { enabled: true },
      organizationHooks: {
        // Before creating a team
        beforeCreateTeam: async ({ team, user, organization }) => {
          // Validate team name, apply naming conventions
          return {
            data: {
              ...team,
              name: team.name.toLowerCase().replace(/\s+/g, "-"),
            },
          };
        },

        // After creating a team
        afterCreateTeam: async ({ team, user, organization }) => {
          // Create default team resources, channels, etc.
          await createDefaultTeamResources(team.id);
        },

        // Before updating a team
        beforeUpdateTeam: async ({ team, updates, user, organization }) => {
          // Validate updates, apply business rules
          return {
            data: {
              ...updates,
              name: updates.name?.toLowerCase(),
            },
          };
        },

        // After updating a team
        afterUpdateTeam: async ({ team, user, organization }) => {
          await syncTeamChangesToExternalSystems(team);
        },

        // Before deleting a team
        beforeDeleteTeam: async ({ team, user, organization }) => {
          // Backup team data, notify members
          await backupTeamData(team.id);
        },

        // After deleting a team
        afterDeleteTeam: async ({ team, user, organization }) => {
          await cleanupTeamResources(team.id);
        },

        // Team member operations
        beforeAddTeamMember: async ({
          teamMember,
          team,
          user,
          organization,
        }) => {
          // Validate team membership limits, permissions
          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,
        }) => {
          // Backup user's team-specific data
          await backupTeamMemberData(user.id, team.id);
        },

        afterRemoveTeamMember: async ({
          teamMember,
          team,
          user,
          organization,
        }) => {
          await revokeTeamAccess(user.id, team.id);
        },
      },
    }),
  ],
});

钩子错误处理

【Hook Error Handling】

所有钩子都支持错误处理。在 before 钩子中抛出错误将阻止操作继续进行:

【All hooks support error handling. Throwing an error in a before hook will prevent the operation from proceeding:】

auth.ts
import { APIError } from "better-auth/api";

export const auth = betterAuth({
  plugins: [
    organization({
      organizationHooks: {
        beforeAddMember: async ({ member, user, organization }) => {
          // Check if user has pending violations
          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 }) => {
          // Validate team name uniqueness
          const existingTeam = await findTeamByName(team.name, organization.id);
          if (existingTeam) {
            throw new APIError("BAD_REQUEST", {
              message: "Team name already exists in this organization",
            });
          }
        },
      },
    }),
  ],
});

列出用户的组织

【List User's Organizations】

要列出用户所属的组织,可以使用 useListOrganizations 钩子。它实现了一种响应式方法来获取用户所属于的组织。

【To list the organizations that a user is a member of, you can use useListOrganizations hook. It implements a reactive way to get the organizations that the user is a member of.】

client.tsx
import { authClient } from "@/lib/auth-client"

function App(){
const { data: organizations } = authClient.useListOrganizations()
return (
  <div>
    {organizations.map((org) => (
      <p>{org.name}</p>
    ))}
  </div>)
}
page.svelte
<script lang="ts">
  import { authClient } from "$lib/auth-client";
  const organizations = authClient.useListOrganizations();
</script>

<h1>Organizations</h1>

{#if $organizations.isPending}

  <p>Loading...</p>
{:else if !$organizations.data?.length}
  <p>No organizations found.</p>
{:else}
  <ul>
    {#each $organizations.data as organization}
      <li>{organization.name}</li>
    {/each}
  </ul>
{/if}
organization.vue
<script lang="ts">;
export default {
    setup() {
        const organizations = authClient.useListOrganizations()
        return { organizations };
    }
};
</script>

<template>
    <div>
        <h1>Organizations</h1>
        <div v-if="organizations.isPending">Loading...</div>
        <div v-else-if="organizations.data === null">No organizations found.</div>
        <ul v-else>
            <li v-for="organization in organizations.data" :key="organization.id">
                {{ organization.name }}
            </li>
        </ul>
    </div>
</template>

或者,如果你不想使用 hook,你可以调用 organization.list

【Or alternatively, you can call organization.list if you don't want to use a hook.】

GET
/organization/list
const { data, error } = await authClient.organization.list();

活跃组织

【Active Organization】

活跃组织是用户当前正在使用的工作区。默认情况下,当用户登录时,活跃组织被设置为 null。你可以将活跃组织设置到用户会话中。

【Active organization is the workspace the user is currently working on. By default when the user is signed in the active organization is set to null. You can set the active organization to the user session.】

你不一定总是希望在会话中保持活动组织。 你可以仅在客户端管理活动组织。例如, 不同的标签页可以有不同的活动组织。

设置活动组织

【Set Active Organization】

你可以通过调用 organization.setActive 函数来设置活跃组织。这将为用户会话设置活跃组织。

【You can set the active organization by calling the organization.setActive function. It'll set the active organization for the user session.】

在某些应用中,你可能希望能够取消设置一个活动的组织。在这种情况下,你可以调用此端点,并将 organizationId 设置为 null

POST
/organization/set-active
const { data, error } = await authClient.organization.setActive({    organizationId: "org-id",    organizationSlug: "org-slug",});
PropDescriptionType
organizationId?
The organization ID to set as active. It can be null to unset the active organization.
string | null
organizationSlug?
The organization slug to set as active. It can be null to unset the active organization if organizationId is not provided.
string

要在会话创建时自动设置一个活动组织,你可以使用 数据库钩子。你需要实现逻辑来确定将哪个组织设置为初始的活动组织。

【To automatically set an active organization when a session is created, you can use database hooks. You'll need to implement logic to determine which organization to set as the initial active organization.】

auth.ts
export const auth = betterAuth({
  databaseHooks: {
    session: {
      create: {
        before: async (session) => {
          // Implement your custom logic to set initial active organization
          const organization = await getInitialOrganization(session.userId);
          return {
            data: {
              ...session,
              activeOrganizationId: organization?.id,
            },
          };
        },
      },
    },
  },
});

使用主动组织

【Use Active Organization】

要获取用户的活动组织,可以调用 useActiveOrganization 钩子。它会返回用户的活动组织。每当活动组织发生变化时,该钩子会重新评估并返回新的活动组织。

【To retrieve the active organization for the user, you can call the useActiveOrganization hook. It returns the active organization for the user. Whenever the active organization changes, the hook will re-evaluate and return the new active organization.】

client.tsx
import { authClient } from "@/lib/auth-client"

function App(){
    const { data: activeOrganization } = authClient.useActiveOrganization()
    return (
        <div>
            {activeOrganization ? <p>{activeOrganization.name}</p> : null}
        </div>
    )
}
client.tsx
<script lang="ts">
import { authClient } from "$lib/auth-client";
const activeOrganization = authClient.useActiveOrganization();
</script>

<h2>Active Organization</h2>

{#if $activeOrganization.isPending}
<p>Loading...</p>
{:else if $activeOrganization.data === null}
<p>No active organization found.</p>
{:else}
<p>{$activeOrganization.data.name}</p>
{/if}
organization.vue
<script lang="ts">;
export default {
    setup() {
        const activeOrganization = authClient.useActiveOrganization();
        return { activeOrganization };
    }
};
</script>

<template>
    <div>
        <h2>Active organization</h2>
        <div v-if="activeOrganization.isPending">Loading...</div>
        <div v-else-if="activeOrganization.data === null">No active organization.</div>
        <div v-else>
            {{ activeOrganization.data.name }}
        </div>
    </div>
</template>

获取完整组织

【Get Full Organization】

要获取组织的完整信息,你可以使用 getFullOrganization 函数。默认情况下,如果你不传递任何属性,它将使用当前活动的组织。

【To get the full details of an organization, you can use the getFullOrganization function. By default, if you don't pass any properties, it will use the active organization.】

GET
/organization/get-full-organization
const { data, error } = await authClient.organization.getFullOrganization({    query: {        organizationId: "org-id",        organizationSlug: "org-slug",        membersLimit: 100,    },});
PropDescriptionType
organizationId?
The organization ID to get. By default, it will use the active organization.
string
organizationSlug?
The organization slug to get.
string
membersLimit?
The limit of members to get. By default, it uses the membershipLimit option which defaults to 100.
number

更新组织

【Update Organization】

要更新组织信息,你可以使用 organization.update

【To update organization info, you can use organization.update

POST
/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",});
PropDescriptionType
data
A partial list of data to update the organization.
Object
data.name?
The name of the organization.
string
data.slug?
The slug of the organization.
string
data.logo?
The logo of the organization.
string
data.metadata?
The metadata of the organization.
Record<string, any> | null
organizationId?
The organization ID. to update.
string

删除组织

【Delete Organization】

要删除用户拥有的组织,可以使用 organization.delete

【To remove user owned organization, you can use organization.delete

POST
/organization/delete
const { data, error } = await authClient.organization.delete({    organizationId: "org-id", // required});
PropDescriptionType
organizationId
The organization ID to delete.
string

如果用户在指定组织中具有必要的权限(默认:角色为所有者),则所有成员、邀请和组织信息将被删除。

【If the user has the necessary permissions (by default: role is owner) in the specified organization, all members, invitations and organization information will be removed.】

你可以通过 organizationDeletion 选项配置组织删除的处理方式:

【You can configure how organization deletion is handled through organizationDeletion option:】

const auth = betterAuth({
  plugins: [
    organization({
      disableOrganizationDeletion: true, //to disable it altogether
      organizationHooks: {
        beforeDeleteOrganization: async (data, request) => {
          // a callback to run before deleting org
        },
        afterDeleteOrganization: async (data, request) => {
          // a callback to run after deleting org
        },
      },
    }),
  ],
});

邀请

【Invitations】

要将成员添加到组织,我们首先需要向用户发送邀请。用户将收到带有邀请链接的电子邮件或短信。一旦用户接受邀请,他们将被添加到组织中。

【To add a member to an organization, we first need to send an invitation to the user. The user will receive an email/sms with the invitation link. Once the user accepts the invitation, they will be added to the organization.】

设置邀请邮件

【Setup Invitation Email】

要邀请成员工作,我们首先需要向 better-auth 实例提供 sendInvitationEmail。该函数负责向用户发送邀请邮件。

【For member invitation to work we first need to provide sendInvitationEmail to the better-auth instance. This function is responsible for sending the invitation email to the user.】

你需要构建并发送邀请链接给用户。链接中应包含邀请 ID,当用户点击时,将与 acceptInvitation 函数一起使用该 ID。

【You'll need to construct and send the invitation link to the user. The link should include the invitation ID, which will be used with the acceptInvitation function when the user clicks on it.】

auth.ts
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,
        });
      },
    }),
  ],
});

发送邀请

【Send Invitation】

要邀请用户加入组织,可以使用客户端提供的 invite 函数。invite 函数接受一个包含以下属性的对象:

【To invite users to an organization, you can use the invite function provided by the client. The invite function takes an object with the following properties:】

POST
/organization/invite-member
const { data, error } = await authClient.organization.inviteMember({    email: "example@gmail.com", // required    role: "member", // required    organizationId: "org-id",    resend: true,    teamId: "team-id",});
PropDescriptionType
email
The email address of the user to invite.
string
role
The role(s) to assign to the user. It can be admin, member, owner
string | string[]
organizationId?
The organization ID to invite the user to. Defaults to the active organization.
string
resend?
Resend the invitation email, if the user is already invited.
boolean
teamId?
The team ID to invite the user to.
string
  • 如果用户已经是该组织的成员,邀请将被取消。 - 如果用户已经被邀请加入该组织,除非将 resend 设置为 true,否则不会再次发送邀请。 - 如果 cancelPendingInvitationsOnReInvite 设置为 true,当用户已经被邀请加入该组织且发送了新的邀请时,该邀请将被取消。

接受邀请

【Accept Invitation】

当用户收到邀请邮件时,他们可以点击邀请链接来接受邀请。邀请链接应包含邀请 ID,该 ID 将用于接受邀请。

【When a user receives an invitation email, they can click on the invitation link to accept the invitation. The invitation link should include the invitation ID, which will be used to accept the invitation.】

确保在用户登录后调用 acceptInvitation 函数。

【Make sure to call the acceptInvitation function after the user is logged in.】

POST
/organization/accept-invitation
const { data, error } = await authClient.organization.acceptInvitation({    invitationId: "invitation-id", // required});
PropDescriptionType
invitationId
The ID of the invitation to accept.
string

电子邮件验证要求

【Email Verification Requirement】

如果在你的组织配置中启用了 requireEmailVerificationOnInvitation 选项,用户必须先验证他们的电子邮件地址才能接受邀请。这增加了一层额外的安全保障,以确保只有经过验证的用户才能加入你的组织。

【If the requireEmailVerificationOnInvitation option is enabled in your organization configuration, users must verify their email address before they can accept invitations. This adds an extra security layer to ensure that only verified users can join your organization.】

auth.ts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    organization({
      requireEmailVerificationOnInvitation: true, 
      async sendInvitationEmail(data) {
        // ... your email sending logic
      },
    }),
  ],
});

取消邀请

【Cancel Invitation】

如果用户已发送邀请,你可以使用此方法取消它。

【If a user has sent out an invitation, you can use this method to cancel it.】

如果你想了解用户如何拒绝邀请,你可以在这里找到相关信息。

【If you're looking for how a user can reject an invitation, you can find that here.】

POST
/organization/cancel-invitation
await authClient.organization.cancelInvitation({    invitationId: "invitation-id", // required});
PropDescriptionType
invitationId
The ID of the invitation to cancel.
string

拒绝邀请

【Reject Invitation】

如果此用户已经收到邀请,但想要拒绝,此方法可以通过拒绝邀请来实现。

【If this user has received an invitation, but wants to decline it, this method will allow you to do so by rejecting it.】

POST
/organization/reject-invitation
await authClient.organization.rejectInvitation({    invitationId: "invitation-id", // required});
PropDescriptionType
invitationId
The ID of the invitation to reject.
string

就像接受邀请一样,拒绝邀请也需要在启用 requireEmailVerificationOnInvitation 选项时进行电子邮件验证。电子邮件未验证的用户在尝试拒绝邀请时会收到错误信息。

获取邀请

【Get Invitation】

要获取邀请函,你可以使用客户端提供的 organization.getInvitation 函数。你需要将邀请 ID 作为查询参数提供。

【To get an invitation you can use the organization.getInvitation function provided by the client. You need to provide the invitation id as a query parameter.】

GET
/organization/get-invitation
const { data, error } = await authClient.organization.getInvitation({    query: {        id: "invitation-id", // required    },});
PropDescriptionType
id
The ID of the invitation to get.
string

列出邀请

【List Invitations】

要列出特定组织的所有邀请,你可以使用客户端提供的 listInvitations 函数。

【To list all invitations for a given organization you can use the listInvitations function provided by the client.】

GET
/organization/list-invitations
const { data, error } = await authClient.organization.listInvitations({    query: {        organizationId: "organization-id",    },});
PropDescriptionType
organizationId?
An optional ID of the organization to list invitations for. If not provided, will default to the user's active organization.
string

列出用户邀请

【List user invitations】

要列出给定用户的所有邀请,你可以使用客户端提供的 listUserInvitations 函数。

【To list all invitations for a given user you can use the listUserInvitations function provided by the client.】

auth-client.ts
const invitations = await authClient.organization.listUserInvitations();

在服务器上,你可以将用户 ID 作为查询参数传递。

【On the server, you can pass the user ID as a query parameter.】

api.ts
const invitations = await auth.api.listUserInvitations({
  query: {
    email: "user@example.com",
  },
});

email 查询参数仅在服务器上可用,用于查询特定用户的邀请。

成员

【Members】

列表成员

【List Members】

要列出一个组织的所有成员,你可以使用 listMembers 函数。

【To list all members of an organization you can use the listMembers function.】

GET
/organization/list-members
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",    },});
PropDescriptionType
organizationId?
An optional organization ID to list members for. If not provided, will default to the user's active organization.
string
limit?
The limit of members to return.
number
offset?
The offset to start from.
number
sortBy?
The field to sort by.
string
sortDirection?
The direction to sort by.
"asc" | "desc"
filterField?
The field to filter by.
string
filterOperator?
The operator to filter by.
"eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "contains"
filterValue?
The value to filter by.
string

移除成员

【Remove Member】

要移除,你可以使用 organization.removeMember

【To remove you can use organization.removeMember

POST
/organization/remove-member
const { data, error } = await authClient.organization.removeMember({    memberIdOrEmail: "user@example.com", // required    organizationId: "org-id",});
PropDescriptionType
memberIdOrEmail
The ID or email of the member to remove.
string
organizationId?
The ID of the organization to remove the member from. If not provided, the active organization will be used.
string

更新成员角色

【Update Member Role】

要更新组织中成员的角色,可以使用 organization.updateMemberRole。如果用户有权限更新该成员的角色,角色将会被更新。

【To update the role of a member in an organization, you can use the organization.updateMemberRole. If the user has the permission to update the role of the member, the role will be updated.】

POST
/organization/update-member-role
await authClient.organization.updateMemberRole({    role: ["admin", "sale"], // required    memberId: "member-id", // required    organizationId: "organization-id",});
PropDescriptionType
role
The new role to be applied. This can be a string or array of strings representing the roles.
string | string[]
memberId
The member id to apply the role update to.
string
organizationId?
An optional organization ID which the member is a part of to apply the role update. If not provided, you must provide session headers to get the active organization.
string

获取活跃成员

【Get Active Member】

要获取当前活跃组织的成员,你可以使用 organization.getActiveMember 函数。该函数将返回用户在其活跃组织中的成员详细信息。

【To get the current member of the active organization you can use the organization.getActiveMember function. This function will return the user's member details in their active organization.】

GET
/organization/get-active-member
const { data: member, error } = await authClient.organization.getActiveMember();

获取活跃会员角色

【Get Active Member Role】

要获取当前活动组织的角色成员,你可以使用 organization.getActiveMemberRole 函数。此函数将返回用户在其活动组织中的成员角色。

【To get the current role member of the active organization you can use the organization.getActiveMemberRole function. This function will return the user's member role in their active organization.】

GET
/organization/get-active-member-role
const { data: { role }, error } = await authClient.organization.getActiveMemberRole();

添加成员

【Add Member】

如果你想直接将成员添加到组织中而不发送邀请,可以使用 addMember 函数,该函数只能在服务器上调用。

【If you want to add a member directly to an organization without sending an invitation, you can use the addMember function which can only be invoked on the server.】

const data = await auth.api.addMember({    body: {        userId: "user-id",        role: ["admin", "sale"], // required        organizationId: "org-id",        teamId: "team-id",    },});
PropDescriptionType
userId?
The user ID which represents the user to be added as a member. If null is provided, then it's expected to provide session headers.
string | null
role
The role(s) to assign to the new member.
string | string[]
organizationId?
An optional organization ID to pass. If not provided, will default to the user's active organization.
string
teamId?
An optional team ID to add the member to.
string

请假组织

【Leave Organization】

要离开组织,可以使用 organization.leave 函数。此函数会将当前用户从组织中移除。

【To leave organization you can use organization.leave function. This function will remove the current user from the organization.】

POST
/organization/leave
await authClient.organization.leave({    organizationId: "organization-id", // required});
PropDescriptionType
organizationId
The organization ID for the member to leave.
string

访问控制

【Access Control】

该组织插件提供了非常灵活的访问控制系统。你可以根据用户在组织中的角色来控制其访问权限。你可以根据用户的角色定义自己的权限集合。

【The organization plugin provides a very flexible access control system. You can control the access of the user based on the role they have in the organization. You can define your own set of permissions based on the role of the user.】

角色

【Roles】

默认情况下,组织中有三种角色:

【By default, there are three roles in the organization:】

owner:默认情况下创建该组织的用户。拥有者对组织拥有完全控制权,并可以执行任何操作。

admin:具有管理员角色的用户对组织拥有完全控制权,但无法删除组织或更改所有者。

member:拥有成员角色的用户对组织的控制权限有限。他们只能读取组织数据,无法创建、更新或删除资源。

一个用户可以拥有多个角色。多个角色以逗号(",")分隔存储为字符串。

权限

【Permissions】

默认情况下,有三个资源,每个资源有两到三个操作。

【By default, there are three resources, and these have two to three actions.】

组织:

update delete

成员

create update delete

邀请

create cancel

所有者对所有资源和操作拥有完全控制权。管理员对所有资源拥有完全控制权,但不能删除组织或更改所有者。成员除了读取数据外,对这些操作没有控制权。

【The owner has full control over all the resources and actions. The admin has full control over all the resources except for deleting the organization or changing the owner. The member has no control over any of those actions other than reading the data.】

自定义权限

【Custom Permissions】

该插件提供了一种简便的方法,让你可以为每个角色定义自己的权限集。

【The plugin provides an easy way to define your own set of permissions for each role.】

创建访问控制

你首先需要通过调用 createAccessControl 函数并传入语句对象来创建访问控制器。语句对象应以资源名称作为键,操作数组作为值。

permissions.ts
import { createAccessControl } from "better-auth/plugins/access";

/**
 * make sure to use `as const` so typescript can infer the type correctly
 */
const statement = { 
    project: ["create", "share", "update", "delete"], 
} as const; 

const ac = createAccessControl(statement); 

创建角色

一旦你创建了访问控制器,就可以创建具有你定义的权限的角色。

permissions.ts
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 并将其与你的新声明合并,同时将角色的权限集合与默认角色合并。

permissions.ts
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, 
});

将角色传递给插件

一旦创建了角色,就可以将它们传递给组织插件,无论是在客户端还是服务器端。

auth.ts
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
            }
        }),
    ],
});

你还需要将访问控制器和角色传递给客户端插件。

auth-client
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
            }
        })
  ]
})

访问控制使用

【Access Control Usage】

有权限

你可以使用 api 提供的 hasPermission 操作来检查用户的权限。

【You can use the hasPermission action provided by the api to check the permission of the user.】

api.ts
import { auth } from "@/auth";

await auth.api.hasPermission({
  headers: await headers(),
  body: {
    permissions: {
      project: ["create"], // This must match the structure in your access control
    },
  },
});

// You can also check multiple resource permissions at the same time
await auth.api.hasPermission({
  headers: await headers(),
  body: {
    permissions: {
      project: ["create"], // This must match the structure in your access control
      sale: ["create"],
    },
  },
});

如果你想从服务器检查客户端用户的权限,可以使用客户端提供的 hasPermission 函数。

【If you want to check the permission of the user on the client from the server you can use the hasPermission function provided by the client.】

auth-client.ts
const canCreateProject = await authClient.organization.hasPermission({
  permissions: {
    project: ["create"],
  },
});

// You can also check multiple resource permissions at the same time
const canCreateProjectAndCreateSale =
  await authClient.organization.hasPermission({
    permissions: {
      project: ["create"],
      sale: ["create"],
    },
  });

检查角色权限

一旦你定义了角色和权限,为了避免从服务器检查权限,你可以使用客户端提供的 checkRolePermission 函数。

【Once you have defined the roles and permissions to avoid checking the permission from the server you can use the checkRolePermission function provided by the client.】

auth-client.ts
const canCreateProject = authClient.organization.checkRolePermission({
  permissions: {
    organization: ["delete"],
  },
  role: "admin",
});

// You can also check multiple resource permissions at the same time
const canCreateProjectAndCreateSale =
  authClient.organization.checkRolePermission({
    permissions: {
      organization: ["delete"],
      member: ["delete"],
    },
    role: "admin",
  });

这将不包括任何动态角色,因为所有操作都是在客户端同步运行的。 请使用 hasPermission API 来包含对任何动态角色和权限的检查。


动态访问控制

【Dynamic Access Control】

动态访问控制允许你在运行时为组织创建角色。这是通过将创建的角色及与组织相关的权限存储在数据库表中实现的。

【Dynamic access control allows you to create roles at runtime for organizations. This is achieved by storing the created roles and permissions associated with an organization in a database table.】

启用动态访问控制

【Enabling Dynamic Access Control】

要启用动态访问控制,请将 dynamicAccessControl 配置选项传递给服务器和客户端插件,并将 enabled 设置为 true

【To enable dynamic access control, pass the dynamicAccessControl configuration option with enabled set to true to both server and client plugins.】

确保你已经在服务器身份验证插件上预先定义了一个 ac 实例。这很重要,因为这是我们推断可用权限的方式。

【Ensure you have pre-defined an ac instance on the server auth plugin. This is important as this is how we can infer the permissions that are available for use.】

auth.ts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { ac } from "@/auth/permissions";

export const auth = betterAuth({
    plugins: [ 
        organization({ 
            ac, // Must be defined in order for dynamic access control to work
            dynamicAccessControl: { 
              enabled: true, 
            }, 
        }) 
    ] 
})
auth-client.ts
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 接口来检测任何动态角色。

创建角色

【Creating a role】

要在运行时为组织创建一个新角色,你可以使用 createRole 函数。

【To create a new role for an organization at runtime, you can use the createRole function.】

只有拥有包含 ac 资源且具有 create 权限的角色的用户才能创建新角色。默认情况下,只有 adminowner 角色具有此权限。你也不能添加当前组织中你的角色无法访问的权限。

【Only users with roles which contain the ac resource with the create permission can create a new role. By default, only the admin and owner roles have this permission. You also cannot add permissions that your current role in that organization can't already access.】

POST
/organization/create-role
// To use custom resources or permissions,// make sure they are defined in the `ac` instance of your organization config.const permission = {  project: ["create", "update", "delete"]}await authClient.organization.createRole({    role: "my-unique-role", // required    permission: permission,    organizationId: "organization-id",});
PropDescriptionType
role
A unique name of the role to create.
string
permission?
The permissions to assign to the role.
Record<string, string[]>
organizationId?
The organization ID which the role will be created in. Defaults to the active organization.
string

现在你可以自由调用 updateMemberRole 来使用你新创建的角色更新成员的角色!

【Now you can freely call updateMemberRole to update the role of a member with your newly created role!】

删除角色

【Deleting a role】

要删除角色,你可以使用 deleteRole 函数,然后提供 roleNameroleId 参数,以及 organizationId 参数。

【To delete a role, you can use the deleteRole function, then provide either a roleName or roleId parameter along with the organizationId parameter.】

POST
/organization/delete-role
await authClient.organization.deleteRole({    roleName: "my-role",    roleId: "role-id",    organizationId: "organization-id",});
PropDescriptionType
roleName?
The name of the role to delete. Alternatively, you can pass a roleId parameter instead.
string
roleId?
The id of the role to delete. Alternatively, you can pass a roleName parameter instead.
string
organizationId?
The organization ID which the role will be deleted in. Defaults to the active organization.
string

列出角色

【Listing roles】

要列出角色,你可以使用 listOrgRoles 函数。 这需要成员拥有 ac 资源的 read 权限才能列出角色。

【To list roles, you can use the listOrgRoles function. This requires the ac resource with the read permission for the member to be able to list roles.】

GET
/organization/list-roles
const { data: roles, error } = await authClient.organization.listRoles({    query: {        organizationId: "organization-id",    },});
PropDescriptionType
organizationId?
The organization ID which the roles are under to list. Defaults to the user's active organization.
string

获取特定角色

【Getting a specific role】

要获取特定角色,你可以使用 getOrgRole 函数,并传入 roleNameroleId 参数。 这需要成员具有 ac 资源的 read 权限才能获取角色。

【To get a specific role, you can use the getOrgRole function and pass either a roleName or roleId parameter. This requires the ac resource with the read permission for the member to be able to get a role.】

GET
/organization/get-role
const { data: role, error } = await authClient.organization.getRole({    query: {        roleName: "my-role",        roleId: "role-id",        organizationId: "organization-id",    },});
PropDescriptionType
roleName?
The name of the role to get. Alternatively, you can pass a roleId parameter instead.
string
roleId?
The id of the role to get. Alternatively, you can pass a roleName parameter instead.
string
organizationId?
The organization ID from which the role will be retrieved. Defaults to the active organization.
string

更新角色

【Updating a role】

要更新角色,你可以使用 updateOrgRole 函数,并传入 roleNameroleId 参数。

【To update a role, you can use the updateOrgRole function and pass either a roleName or roleId parameter.】

POST
/organization/update-role
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",    },});
PropDescriptionType
roleName?
The name of the role to update. Alternatively, you can pass a roleId parameter instead.
string
roleId?
The id of the role to update. Alternatively, you can pass a roleName parameter instead.
string
organizationId?
The organization ID which the role will be updated in. Defaults to the active organization.
string
data
The data which will be updated
Object
data.permission?
Optionally update the permissions of the role.
Record<string, string[]>
data.roleName?
Optionally update the name of the role.
string

配置选项

【Configuration Options】

下面是可以传递给 dynamicAccessControl 对象的选项列表。

【Below is a list of options that can be passed to the dynamicAccessControl object.】

enabled

此选项用于启用或禁用动态访问控制。默认情况下,它是禁用的。

【This option is used to enable or disable dynamic access control. By default, it is disabled.】

organization({
  dynamicAccessControl: {
    enabled: true
  }
})

maximumRolesPerOrganization

此选项用于限制一个组织可以创建的角色数量。

【This option is used to limit the number of roles that can be created for an organization.】

默认情况下,一个组织可以创建的角色数量没有上限。

【By default, the maximum number of roles that can be created for an organization is infinite.】

organization({
  dynamicAccessControl: {
    maximumRolesPerOrganization: 10
  }
})

你也可以传递一个返回数字的函数。

【You can also pass a function that returns a number.】

organization({
  dynamicAccessControl: {
    maximumRolesPerOrganization: async (organizationId) => { 
      const organization = await getOrganization(organizationId); 
      return organization.plan === "pro" ? 100 : 10; 
    } 
  }
})

附加字段

【Additional Fields】

要向 organizationRole 表中添加额外字段,可以将 additionalFields 配置选项传递给 organization 插件。

【To add additional fields to the organizationRole table, you can pass the additionalFields configuration option to the organization plugin.】

organization({
  schema: {
    organizationRole: {
      additionalFields: {
        // Role colors!
        color: {
          type: "string",
          defaultValue: "#ffffff",
        },
        //... other fields
      },
    },
  },
})

那么,如果你还没有使用 inferOrgAdditionalFields 来推断附加字段,你可以使用它来推断这些附加字段。

【Then, if you don't already use inferOrgAdditionalFields to infer the additional fields, you can use it to infer the additional fields.】

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { organizationClient, inferOrgAdditionalFields } from "better-auth/client/plugins"
import type { auth } from "./auth"

export const authClient = createAuthClient({
    plugins: [
        organizationClient({
            schema: inferOrgAdditionalFields<typeof auth>()
        })
    ]
})

Otherwise, you can pass the schema values directly, the same way you do on the org plugin in the server.

auth-client.ts
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】

团队允许你在组织内对成员进行分组。团队功能提供了额外的组织结构,并可用于在更细的层面上管理权限。

【Teams allow you to group members within an organization. The teams feature provides additional organization structure and can be used to manage permissions at a more granular level.】

启用团队

【Enabling Teams】

要启用团队功能,请将 teams 配置选项传递给服务器和客户端插件:

【To enable teams, pass the teams configuration option to both server and client plugins:】

auth.ts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    organization({
      teams: {
        enabled: true,
        maximumTeams: 10, // Optional: limit teams per organization
        allowRemovingAllTeams: false, // Optional: prevent removing the last team
      },
    }),
  ],
});
auth-client.ts
import { createAuthClient } from "better-auth/client";
import { organizationClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  plugins: [
    organizationClient({
      teams: {
        enabled: true,
      },
    }),
  ],
});

团队管理

【Managing Teams】

创建团队

【Create Team】

在组织内创建一个新团队:

【Create a new team within an organization:】

POST
/organization/create-team
const { data, error } = await authClient.organization.createTeam({    name: "my-team", // required    organizationId: "organization-id",});
PropDescriptionType
name
The name of the team.
string
organizationId?
The organization ID which the team will be created in. Defaults to the active organization.
string

列出团队

【List Teams】

获取组织中的所有团队:

【Get all teams in an organization:】

GET
/organization/list-teams
const { data, error } = await authClient.organization.listTeams({    query: {        organizationId: "organization-id",    },});
PropDescriptionType
organizationId?
The organization ID which the teams are under to list. Defaults to the user's active organization.
string

更新团队

【Update Team】

更新团队信息:

【Update a team's details:】

POST
/organization/update-team
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(),    },});
PropDescriptionType
teamId
The ID of the team to be updated.
string
data
A partial object containing options for you to update.
Object
data.name?
The name of the team to be updated.
string
data.organizationId?
The organization ID which the team falls under.
string
data.createdAt?
The timestamp of when the team was created.
Date
data.updatedAt?
The timestamp of when the team was last updated.
Date

移除团队

【Remove Team】

从组织中删除一个团队:

【Delete a team from an organization:】

POST
/organization/remove-team
const { data, error } = await authClient.organization.removeTeam({    teamId: "team-id", // required    organizationId: "organization-id",});
PropDescriptionType
teamId
The team ID of the team to remove.
string
organizationId?
The organization ID which the team falls under. If not provided, it will default to the user's active organization.
string

设置活动团队

【Set Active Team】

将指定的团队设置为当前活动团队。如果 teamIdnull,则取消设置当前活动团队。

【Sets the given team as the current active team. If teamId is null the current active team is unset.】

POST
/organization/set-active-team
const { data, error } = await authClient.organization.setActiveTeam({    teamId: "team-id",});
PropDescriptionType
teamId?
The team ID of the team to set as the current active team.
string

列出用户团队

【List User Teams】

列出当前用户所属的所有团队。

【List all teams that the current user is a part of.】

GET
/organization/list-user-teams
const { data, error } = await authClient.organization.listUserTeams();

列出团队成员

【List Team Members】

列出给定团队的成员。

【List the members of the given team.】

POST
/organization/list-team-members
const { data, error } = await authClient.organization.listTeamMembers({    query: {        teamId: "team-id",    },});
PropDescriptionType
teamId?
The team whose members we should return. If this is not provided the members of the current active team get returned.
string

添加团队成员

【Add Team Member】

向团队添加成员。

【Add a member to a team.】

POST
/organization/add-team-member
const { data, error } = await authClient.organization.addTeamMember({    teamId: "team-id", // required    userId: "user-id", // required});
PropDescriptionType
teamId
The team the user should be a member of.
string
userId
The user ID which represents the user to be added as a member.
string

移除团队成员

【Remove Team Member】

将成员从团队中移除。

【Remove a member from a team.】

POST
/organization/remove-team-member
const { data, error } = await authClient.organization.removeTeamMember({    teamId: "team-id", // required    userId: "user-id", // required});
PropDescriptionType
teamId
The team the user should be removed from.
string
userId
The user which should be removed from the team.
string

团队权限

【Team Permissions】

团队遵循组织的权限系统。要管理团队,用户需要以下权限:

【Teams follow the organization's permission system. To manage teams, users need the following permissions:】

  • team:create - 创建新团队
  • team:update - 更新团队详情
  • team:delete - 删除团队

默认情况下:

【By default:】

  • 组织所有者和管理员可以管理团队
  • 普通成员无法创建、更新或删除团队

团队配置选项

【Team Configuration Options】

团队功能支持多种配置选项:

【The teams feature supports several configuration options:】

  • maximumTeams:限制每个组织的团队数量

    teams: {
      enabled: true,
      maximumTeams: 10 // Fixed number
      // OR
      maximumTeams: async ({ organizationId, session }, ctx) => {
        // Dynamic limit based on organization plan
        const plan = await getPlan(organizationId)
        return plan === 'pro' ? 20 : 5
      },
      maximumMembersPerTeam: 10 // Fixed number
      // OR
      maximumMembersPerTeam: async ({ teamId, session, organizationId }, ctx) => {
        // Dynamic limit based on team plan
        const plan = await getPlan(organizationId, teamId)
        return plan === 'pro' ? 50 : 10
      },
    }
  • allowRemovingAllTeams:控制是否可以移除最后一个团队

    teams: {
      enabled: true,
      allowRemovingAllTeams: false // Prevent removing the last team
    }

团队成员

【Team Members】

在邀请成员加入组织时,你可以指定一个团队:

【When inviting members to an organization, you can specify a team:】

await authClient.organization.inviteMember({
  email: "user@example.com",
  role: "member",
  teamId: "team-id",
});

受邀成员在接受邀请后将被添加到指定的团队中。

【The invited member will be added to the specified team upon accepting the invitation.】

数据库架构

【Database Schema】

启用团队功能后,数据库中将添加新的 teamteamMember 表。

【When teams are enabled, new team and teamMember tables are added to the database.】

表名:team

【Table Name: team

Field NameTypeKeyDescription
idstringUnique identifier for each team
namestring-The name of the team
organizationIdstringThe ID of the organization
createdAtDate-Timestamp of when the team was created
updatedAtDateTimestamp of when the team was created

表名:teamMember

【Table Name: teamMember

Field NameTypeKeyDescription
idstringUnique identifier for each team member
teamIdstringUnique identifier for each team
userIdstringThe ID of the user
createdAtDate-Timestamp of when the team member was created

架构

【Schema】

该组织插件向数据库添加以下表格:

【The organization plugin adds the following tables to the database:】

组织

【Organization】

表名:organization

【Table Name: organization

Field NameTypeKeyDescription
idstringUnique identifier for each organization
namestring-The name of the organization
slugstring-The slug of the organization
logostringThe logo of the organization
metadatastringAdditional metadata for the organization
createdAtDate-Timestamp of when the organization was created

成员

【Member】

表名:member

【Table Name: member

Field NameTypeKeyDescription
idstringUnique identifier for each member
userIdstringThe ID of the user
organizationIdstringThe ID of the organization
rolestring-The role of the user in the organization
createdAtDate-Timestamp of when the member was added to the organization

邀请

【Invitation】

表名:invitation

【Table Name: invitation

Field NameTypeKeyDescription
idstringUnique identifier for each invitation
emailstring-The email address of the user
inviterIdstringThe ID of the inviter
organizationIdstringThe ID of the organization
rolestring-The role of the user in the organization
statusstring-The status of the invitation
createdAtDate-Timestamp of when the invitation was created
expiresAtDate-Timestamp of when the invitation expires

如果启用了团队功能,你需要在邀请表中添加以下字段:

【If teams are enabled, you need to add the following fields to the invitation table:】

Field NameTypeKeyDescription
teamIdstringThe ID of the team

会话

【Session】

表名:session

【Table Name: session

你需要在会话表中添加两个字段来存储活动的组织 ID 和活动的团队 ID。

【You need to add two more fields to the session table to store the active organization ID and the active team ID.】

Field NameTypeKeyDescription
activeOrganizationIdstringThe ID of the active organization
activeTeamIdstringThe ID of the active team

组织角色(可选)

【Organization Role (optional)】

表名:organizationRole

【Table Name: organizationRole

Field NameTypeKeyDescription
idstring-Unique identifier for each organization role
organizationIdstringThe ID of the organization
rolestring-The name of the role
permissionstring-The permission of the role
createdAtDate-Timestamp of when the organization role was created
updatedAtDate-Timestamp of when the organization role was updated

团队(可选)

【Teams (optional)】

表名:team

【Table Name: team

Field NameTypeKeyDescription
idstringUnique identifier for each team
namestring-The name of the team
organizationIdstringThe ID of the organization
createdAtDate-Timestamp of when the team was created
updatedAtDateTimestamp of when the team was created

表名:teamMember

【Table Name: teamMember

Field NameTypeKeyDescription
idstringUnique identifier for each team member
teamIdstringUnique identifier for each team
userIdstringThe ID of the user
createdAtDate-Timestamp of when the team member was created

表名:invitation

【Table Name: invitation

Field NameTypeKeyDescription
teamIdstringThe ID of the team

自定义模式

【Customizing the Schema】

要更改模式表名称或字段,你可以将 schema 选项传递给组织插件。

【To change the schema table name or fields, you can pass schema option to the organization plugin.】

auth.ts
const auth = betterAuth({
  plugins: [
    organization({
      schema: {
        organization: {
          modelName: "organizations", //map the organization table to organizations
          fields: {
            name: "title", //map the name field to title
          },
          additionalFields: {
            // Add a new field to the organization table
            myCustomField: {
              type: "string",
              input: true,
              required: false,
            },
          },
        },
      },
    }),
  ],
});

附加字段

【Additional Fields】

Better Auth v1.3 开始,你可以轻松地向 organizationinvitationmemberteam 表添加自定义字段。

【Starting with Better Auth v1.3, you can easily add custom fields to the organization, invitation, member, and team tables.】

当你向模型添加额外字段时,相关的 API 端点将自动接受并返回这些新属性。例如,如果你向 organization 表添加自定义字段,createOrganization 端点将在其请求和响应负载中根据需要包含该字段。

【When you add extra fields to a model, the relevant API endpoints will automatically accept and return these new properties. For instance, if you add a custom field to the organization table, the createOrganization endpoint will include this field in its request and response payloads as needed.】

auth.ts
const auth = betterAuth({
  plugins: [
    organization({
      schema: {
        organization: {
          additionalFields: {
            myCustomField: {
              type: "string", 
              input: true, 
              required: false, 
            }, 
          },
        },
      },
    }),
  ],
});

要推断附加字段,你可以使用 inferOrgAdditionalFields 函数。该函数将从 auth 对象类型中推断附加字段。

【For inferring the additional fields, you can use the inferOrgAdditionalFields function. This function will infer the additional fields from the auth object type.】

auth-client.ts
import { createAuthClient } from "better-auth/client";
import {
  inferOrgAdditionalFields,
  organizationClient,
} from "better-auth/client/plugins";
import type { auth } from "@/auth"; // import the auth object type only

const client = createAuthClient({
  plugins: [
    organizationClient({
      schema: inferOrgAdditionalFields<typeof auth>(),
    }),
  ],
});

如果你无法导入 auth 对象类型,你可以使用 inferOrgAdditionalFields 函数而不使用泛型。该函数将从 schema 对象中推断额外的字段。

【if you can't import the auth object type, you can use the inferOrgAdditionalFields function without the generic. This function will infer the additional fields from the schema object.】

auth-client.ts
const client = createAuthClient({
  plugins: [
    organizationClient({
      schema: inferOrgAdditionalFields({
        organization: {
          additionalFields: {
            newField: {
              type: "string", 
            }, 
          },
        },
      }),
    }),
  ],
});

//example usage
await client.organization.create({
  name: "Test",
  slug: "test",
  newField: "123", //this should be allowed
  //@ts-expect-error - this field is not available
  unavailableField: "123", //this should be not allowed
});

选项

【Options】

allowUserToCreateOrganization: boolean | ((user: User) => Promise<boolean> | boolean) - 一个函数,用于确定用户是否可以创建组织。默认情况下,它是 true。你可以将其设置为 false 来限制用户创建组织。

organizationLimit: number | ((user: User) => Promise<boolean> | boolean) - 用户允许的最大组织数量。默认情况下为 unlimited。您可以将其设置为任意数字,或一个返回布尔值的函数。如果提供函数,当用户达到组织数量上限时应返回 true(阻止进一步创建),如果未达到上限应返回 false(允许进一步创建)。

creatorRoleadmin | owner - 创建该组织的用户角色。默认是 owner。你可以将其设置为 admin

membershipLimit: 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。启用时,用户必须先验证他们的电子邮件地址,才能接受或拒绝组织邀请。