organization-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSetting Up Organizations
组织设置
When adding organizations to your application, configure the plugin with appropriate limits and permissions.
organizationts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
allowUserToCreateOrganization: true,
organizationLimit: 5, // Max orgs per user
membershipLimit: 100, // Max members per org
}),
],
});Note: After adding the plugin, run to add the required database tables.
npx @better-auth/cli migrate在应用中添加组织功能时,请为插件配置合适的限制与权限。
organizationts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
allowUserToCreateOrganization: true,
organizationLimit: 5, // Max orgs per user
membershipLimit: 100, // Max members per org
}),
],
});注意:添加插件后,请运行以添加所需的数据库表。
npx @better-auth/cli migrateClient-Side Setup
客户端设置
Add the client plugin to access organization methods:
ts
import { createAuthClient } from "better-auth/client";
import { organizationClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [organizationClient()],
});添加客户端插件以调用组织相关方法:
ts
import { createAuthClient } from "better-auth/client";
import { organizationClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [organizationClient()],
});Creating Organizations
创建组织
Organizations are the top-level entity for grouping users. When created, the creator is automatically assigned the role.
ownerts
const createOrg = async () => {
const { data, error } = await authClient.organization.create({
name: "My Company",
slug: "my-company",
logo: "https://example.com/logo.png",
metadata: { plan: "pro" },
});
};组织是用于分组用户的顶级实体。创建组织时,创建者会自动被分配角色。
ownerts
const createOrg = async () => {
const { data, error } = await authClient.organization.create({
name: "My Company",
slug: "my-company",
logo: "https://example.com/logo.png",
metadata: { plan: "pro" },
});
};Controlling Organization Creation
控制组织创建权限
Restrict who can create organizations based on user attributes:
ts
organization({
allowUserToCreateOrganization: async (user) => {
return user.emailVerified === true;
},
organizationLimit: async (user) => {
// Premium users get more organizations
return user.plan === "premium" ? 20 : 3;
},
});根据用户属性限制可创建组织的用户:
ts
organization({
allowUserToCreateOrganization: async (user) => {
return user.emailVerified === true;
},
organizationLimit: async (user) => {
// Premium users get more organizations
return user.plan === "premium" ? 20 : 3;
},
});Creating Organizations on Behalf of Users
代用户创建组织
Administrators can create organizations for other users (server-side only):
ts
await auth.api.createOrganization({
body: {
name: "Client Organization",
slug: "client-org",
userId: "user-id-who-will-be-owner", // `userId` is required
},
});Note: The parameter cannot be used alongside session headers.
userId管理员可代其他用户创建组织(仅服务端支持):
ts
await auth.api.createOrganization({
body: {
name: "Client Organization",
slug: "client-org",
userId: "user-id-who-will-be-owner", // `userId` is required
},
});注意:参数不能与会话头一起使用。
userIdActive Organizations
活跃组织
The active organization is stored in the session and scopes subsequent API calls. Always set an active organization after the user selects one.
ts
const setActive = async (organizationId: string) => {
const { data, error } = await authClient.organization.setActive({
organizationId,
});
};Many endpoints use the active organization when is not provided:
organizationIdts
// These use the active organization automatically
await authClient.organization.listMembers();
await authClient.organization.listInvitations();
await authClient.organization.inviteMember({ email: "user@example.com", role: "member" });活跃组织会存储在会话中,并限定后续API调用的作用域。用户选择组织后,请务必设置活跃组织。
ts
const setActive = async (organizationId: string) => {
const { data, error } = await authClient.organization.setActive({
organizationId,
});
};当未提供时,许多端点会自动使用活跃组织:
organizationIdts
// These use the active organization automatically
await authClient.organization.listMembers();
await authClient.organization.listInvitations();
await authClient.organization.inviteMember({ email: "user@example.com", role: "member" });Getting Full Organization Data
获取完整组织数据
Retrieve the active organization with all its members, invitations, and teams:
ts
const { data } = await authClient.organization.getFullOrganization();
// data.organization, data.members, data.invitations, data.teams获取包含所有成员、邀请及团队信息的活跃组织:
ts
const { data } = await authClient.organization.getFullOrganization();
// data.organization, data.members, data.invitations, data.teamsMembers
成员
Members are users who belong to an organization. Each member has a role that determines their permissions.
成员是属于某一组织的用户。每个成员都拥有一个决定其权限的角色。
Adding Members (Server-Side)
添加成员(服务端)
Add members directly without invitations (useful for admin operations):
ts
await auth.api.addMember({
body: {
userId: "user-id",
role: "member",
organizationId: "org-id",
},
});Note: For client-side member additions, use the invitation system instead.
无需邀请即可直接添加成员(适用于管理员操作):
ts
await auth.api.addMember({
body: {
userId: "user-id",
role: "member",
organizationId: "org-id",
},
});注意:客户端添加成员请使用邀请系统。
Assigning Multiple Roles
分配多个角色
Members can have multiple roles for fine-grained permissions:
ts
await auth.api.addMember({
body: {
userId: "user-id",
role: ["admin", "moderator"],
organizationId: "org-id",
},
});成员可拥有多个角色以实现细粒度权限控制:
ts
await auth.api.addMember({
body: {
userId: "user-id",
role: ["admin", "moderator"],
organizationId: "org-id",
},
});Removing Members
移除成员
Remove members by ID or email:
ts
await authClient.organization.removeMember({
memberIdOrEmail: "user@example.com",
});Important: The last owner cannot be removed. Assign the owner role to another member first.
通过ID或邮箱移除成员:
ts
await authClient.organization.removeMember({
memberIdOrEmail: "user@example.com",
});重要提示:最后一位组织所有者无法被移除。请先将所有者角色分配给其他成员。
Updating Member Roles
更新成员角色
ts
await authClient.organization.updateMemberRole({
memberId: "member-id",
role: "admin",
});ts
await authClient.organization.updateMemberRole({
memberId: "member-id",
role: "admin",
});Membership Limits
成员数量限制
Control the maximum number of members per organization:
ts
organization({
membershipLimit: async (user, organization) => {
if (organization.metadata?.plan === "enterprise") {
return 1000;
}
return 50;
},
});控制每个组织的最大成员数:
ts
organization({
membershipLimit: async (user, organization) => {
if (organization.metadata?.plan === "enterprise") {
return 1000;
}
return 50;
},
});Invitations
邀请
The invitation system allows admins to invite users via email. Configure email sending to enable invitations.
邀请系统允许管理员通过邮箱邀请用户。请配置邮件发送功能以启用邀请。
Setting Up Invitation Emails
设置邀请邮件
ts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { sendEmail } from "./email";
export const auth = betterAuth({
plugins: [
organization({
sendInvitationEmail: async (data) => {
const { email, organization, inviter, invitation } = data;
await sendEmail({
to: email,
subject: `Join ${organization.name}`,
html: `
<p>${inviter.user.name} invited you to join ${organization.name}</p>
<a href="https://yourapp.com/accept-invite?id=${invitation.id}">
Accept Invitation
</a>
`,
});
},
}),
],
});ts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { sendEmail } from "./email";
export const auth = betterAuth({
plugins: [
organization({
sendInvitationEmail: async (data) => {
const { email, organization, inviter, invitation } = data;
await sendEmail({
to: email,
subject: `Join ${organization.name}`,
html: `
<p>${inviter.user.name} invited you to join ${organization.name}</p>
<a href="https://yourapp.com/accept-invite?id=${invitation.id}">
Accept Invitation
</a>
`,
});
},
}),
],
});Sending Invitations
发送邀请
ts
await authClient.organization.inviteMember({
email: "newuser@example.com",
role: "member",
});ts
await authClient.organization.inviteMember({
email: "newuser@example.com",
role: "member",
});Creating Shareable Invitation URLs
创建可分享的邀请链接
For sharing via Slack, SMS, or in-app notifications:
ts
const { data } = await authClient.organization.getInvitationURL({
email: "newuser@example.com",
role: "member",
callbackURL: "https://yourapp.com/dashboard",
});
// Share data.url via any channelNote: This endpoint does not call . Handle delivery yourself.
sendInvitationEmail用于通过Slack、短信或应用内通知分享:
ts
const { data } = await authClient.organization.getInvitationURL({
email: "newuser@example.com",
role: "member",
callbackURL: "https://yourapp.com/dashboard",
});
// Share data.url via any channel注意:该端点不会调用,请自行处理发送。
sendInvitationEmailAccepting Invitations
接受邀请
ts
await authClient.organization.acceptInvitation({
invitationId: "invitation-id",
});ts
await authClient.organization.acceptInvitation({
invitationId: "invitation-id",
});Invitation Configuration
邀请配置
ts
organization({
invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days (default: 48 hours)
invitationLimit: 100, // Max pending invitations per org
cancelPendingInvitationsOnReInvite: true, // Cancel old invites when re-inviting
});ts
organization({
invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days (default: 48 hours)
invitationLimit: 100, // Max pending invitations per org
cancelPendingInvitationsOnReInvite: true, // Cancel old invites when re-inviting
});Roles & Permissions
角色与权限
The plugin provides role-based access control (RBAC) with three default roles:
| Role | Description |
|---|---|
| Full access, can delete organization |
| Can manage members, invitations, settings |
| Basic access to organization resources |
该插件提供基于角色的访问控制(RBAC),包含三个默认角色:
| 角色 | 描述 |
|---|---|
| 完全权限,可删除组织 |
| 可管理成员、邀请及设置 |
| 对组织资源拥有基础访问权限 |
Checking Permissions
检查权限
ts
const { data } = await authClient.organization.hasPermission({
permission: "member:write",
});
if (data?.hasPermission) {
// User can manage members
}ts
const { data } = await authClient.organization.hasPermission({
permission: "member:write",
});
if (data?.hasPermission) {
// User can manage members
}Client-Side Permission Checks
客户端权限检查
For UI rendering without API calls:
ts
const canManageMembers = authClient.organization.checkRolePermission({
role: "admin",
permissions: ["member:write"],
});Note: For dynamic access control, the client side role permission check will not work. Please use the endpoint.
hasPermission无需调用API即可用于UI渲染:
ts
const canManageMembers = authClient.organization.checkRolePermission({
role: "admin",
permissions: ["member:write"],
});注意:对于动态访问控制,客户端角色权限检查将无法工作,请使用端点。
hasPermissionTeams
团队
Teams allow grouping members within an organization.
团队允许在组织内对成员进行分组。
Enabling Teams
启用团队功能
ts
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
teams: {
enabled: true
}
}),
],
});ts
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
teams: {
enabled: true
}
}),
],
});Creating Teams
创建团队
ts
const { data } = await authClient.organization.createTeam({
name: "Engineering",
});ts
const { data } = await authClient.organization.createTeam({
name: "Engineering",
});Managing Team Members
管理团队成员
ts
// Add a member to a team (must be org member first)
await authClient.organization.addTeamMember({
teamId: "team-id",
userId: "user-id",
});
// Remove from team (stays in org)
await authClient.organization.removeTeamMember({
teamId: "team-id",
userId: "user-id",
});ts
// Add a member to a team (must be org member first)
await authClient.organization.addTeamMember({
teamId: "team-id",
userId: "user-id",
});
// Remove from team (stays in org)
await authClient.organization.removeTeamMember({
teamId: "team-id",
userId: "user-id",
});Active Teams
活跃团队
Similar to active organizations, set an active team for the session:
ts
await authClient.organization.setActiveTeam({
teamId: "team-id",
});与活跃组织类似,可为会话设置活跃团队:
ts
await authClient.organization.setActiveTeam({
teamId: "team-id",
});Team Limits
团队数量限制
ts
organization({
teams: {
maximumTeams: 20, // Max teams per org
maximumMembersPerTeam: 50, // Max members per team
allowRemovingAllTeams: false, // Prevent removing last team
}
});ts
organization({
teams: {
maximumTeams: 20, // Max teams per org
maximumMembersPerTeam: 50, // Max members per team
allowRemovingAllTeams: false, // Prevent removing last team
}
});Dynamic Access Control
动态访问控制
For applications needing custom roles per organization at runtime, enable dynamic access control.
对于需要在运行时为每个组织配置自定义角色的应用,请启用动态访问控制。
Enabling Dynamic Access Control
启用动态访问控制
ts
import { organization } from "better-auth/plugins";
import { dynamicAccessControl } from "@better-auth/organization/addons";
export const auth = betterAuth({
plugins: [
organization({
dynamicAccessControl: {
enabled: true
}
}),
],
});ts
import { organization } from "better-auth/plugins";
import { dynamicAccessControl } from "@better-auth/organization/addons";
export const auth = betterAuth({
plugins: [
organization({
dynamicAccessControl: {
enabled: true
}
}),
],
});Creating Custom Roles
创建自定义角色
ts
await authClient.organization.createRole({
role: "moderator",
permission: {
member: ["read"],
invitation: ["read"],
},
});ts
await authClient.organization.createRole({
role: "moderator",
permission: {
member: ["read"],
invitation: ["read"],
},
});Updating and Deleting Roles
更新与删除角色
ts
// Update role permissions
await authClient.organization.updateRole({
roleId: "role-id",
permission: {
member: ["read", "write"],
},
});
// Delete a custom role
await authClient.organization.deleteRole({
roleId: "role-id",
});Note: Pre-defined roles (owner, admin, member) cannot be deleted. Roles assigned to members cannot be deleted until members are reassigned.
ts
// Update role permissions
await authClient.organization.updateRole({
roleId: "role-id",
permission: {
member: ["read", "write"],
},
});
// Delete a custom role
await authClient.organization.deleteRole({
roleId: "role-id",
});注意:预定义角色(owner、admin、member)无法删除。已分配给成员的角色,需先重新分配成员角色后才能删除。
Lifecycle Hooks
生命周期钩子
Execute custom logic at various points in the organization lifecycle:
ts
organization({
hooks: {
organization: {
beforeCreate: async ({ data, user }) => {
// Validate or modify data before creation
return {
data: {
...data,
metadata: { ...data.metadata, createdBy: user.id },
},
};
},
afterCreate: async ({ organization, member }) => {
// Post-creation logic (e.g., send welcome email, create default resources)
await createDefaultResources(organization.id);
},
beforeDelete: async ({ organization }) => {
// Cleanup before deletion
await archiveOrganizationData(organization.id);
},
},
member: {
afterCreate: async ({ member, organization }) => {
await notifyAdmins(organization.id, `New member joined`);
},
},
invitation: {
afterCreate: async ({ invitation, organization, inviter }) => {
await logInvitation(invitation);
},
},
},
});在组织生命周期的各个阶段执行自定义逻辑:
ts
organization({
hooks: {
organization: {
beforeCreate: async ({ data, user }) => {
// Validate or modify data before creation
return {
data: {
...data,
metadata: { ...data.metadata, createdBy: user.id },
},
};
},
afterCreate: async ({ organization, member }) => {
// Post-creation logic (e.g., send welcome email, create default resources)
await createDefaultResources(organization.id);
},
beforeDelete: async ({ organization }) => {
// Cleanup before deletion
await archiveOrganizationData(organization.id);
},
},
member: {
afterCreate: async ({ member, organization }) => {
await notifyAdmins(organization.id, `New member joined`);
},
},
invitation: {
afterCreate: async ({ invitation, organization, inviter }) => {
await logInvitation(invitation);
},
},
},
});Schema Customization
Schema自定义
Customize table names, field names, and add additional fields:
ts
organization({
schema: {
organization: {
modelName: "workspace", // Rename table
fields: {
name: "workspaceName", // Rename fields
},
additionalFields: {
billingId: {
type: "string",
required: false,
},
},
},
member: {
additionalFields: {
department: {
type: "string",
required: false,
},
title: {
type: "string",
required: false,
},
},
},
},
});自定义表名、字段名并添加额外字段:
ts
organization({
schema: {
organization: {
modelName: "workspace", // Rename table
fields: {
name: "workspaceName", // Rename fields
},
additionalFields: {
billingId: {
type: "string",
required: false,
},
},
},
member: {
additionalFields: {
department: {
type: "string",
required: false,
},
title: {
type: "string",
required: false,
},
},
},
},
});Security Considerations
安全注意事项
Owner Protection
所有者保护
- The last owner cannot be removed from an organization
- The last owner cannot leave the organization
- The owner role cannot be removed from the last owner
Always ensure ownership transfer before removing the current owner:
ts
// Transfer ownership first
await authClient.organization.updateMemberRole({
memberId: "new-owner-member-id",
role: "owner",
});
// Then the previous owner can be demoted or removed- 最后一位组织所有者无法被移除
- 最后一位组织所有者无法退出组织
- 无法从最后一位所有者身上移除owner角色
移除当前所有者前,请确保已完成所有权转移:
ts
// Transfer ownership first
await authClient.organization.updateMemberRole({
memberId: "new-owner-member-id",
role: "owner",
});
// Then the previous owner can be demoted or removedOrganization Deletion
组织删除
Deleting an organization removes all associated data (members, invitations, teams). Prevent accidental deletion:
ts
organization({
disableOrganizationDeletion: true, // Disable via config
});Or implement soft delete via hooks:
ts
organization({
hooks: {
organization: {
beforeDelete: async ({ organization }) => {
// Archive instead of delete
await archiveOrganization(organization.id);
throw new Error("Organization archived, not deleted");
},
},
},
});删除组织会移除所有关联数据(成员、邀请、团队)。请防止意外删除:
ts
organization({
disableOrganizationDeletion: true, // Disable via config
});或通过钩子实现软删除:
ts
organization({
hooks: {
organization: {
beforeDelete: async ({ organization }) => {
// Archive instead of delete
await archiveOrganization(organization.id);
throw new Error("Organization archived, not deleted");
},
},
},
});Invitation Security
邀请安全
- Invitations expire after 48 hours by default
- Only the invited email address can accept an invitation
- Pending invitations can be cancelled by organization admins
- 邀请默认48小时后过期
- 仅受邀邮箱可接受邀请
- 组织管理员可取消待处理的邀请
Complete Configuration Example
完整配置示例
ts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { sendEmail } from "./email";
export const auth = betterAuth({
plugins: [
organization({
// Organization limits
allowUserToCreateOrganization: true,
organizationLimit: 10,
membershipLimit: 100,
creatorRole: "owner",
// Slugs
defaultOrganizationIdField: "slug",
// Invitations
invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days
invitationLimit: 50,
sendInvitationEmail: async (data) => {
await sendEmail({
to: data.email,
subject: `Join ${data.organization.name}`,
html: `<a href="https://app.com/invite/${data.invitation.id}">Accept</a>`,
});
},
// Hooks
hooks: {
organization: {
afterCreate: async ({ organization }) => {
console.log(`Organization ${organization.name} created`);
},
},
},
}),
],
});ts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { sendEmail } from "./email";
export const auth = betterAuth({
plugins: [
organization({
// Organization limits
allowUserToCreateOrganization: true,
organizationLimit: 10,
membershipLimit: 100,
creatorRole: "owner",
// Slugs
defaultOrganizationIdField: "slug",
// Invitations
invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days
invitationLimit: 50,
sendInvitationEmail: async (data) => {
await sendEmail({
to: data.email,
subject: `Join ${data.organization.name}`,
html: `<a href="https://app.com/invite/${data.invitation.id}">Accept</a>`,
});
},
// Hooks
hooks: {
organization: {
afterCreate: async ({ organization }) => {
console.log(`Organization ${organization.name} created`);
},
},
},
}),
],
});