用户与账户

除了用户认证功能外,Better Auth 还提供了一套用户管理方法,包括更新用户信息、修改密码等操作。

用户表存储用户的认证数据,点击此处查看数据表结构

用户表可以通过附加字段或插件进行扩展,以存储额外的数据。

更新用户信息

更新用户信息

要更新用户信息,您可以使用客户端提供的 updateUser 函数。该函数接收包含以下属性的对象:

await authClient.updateUser({
    image: "https://example.com/image.jpg",
    name: "John Doe",
})

更改邮箱

要允许用户更改邮箱,首先需要启用默认禁用的 changeEmail 功能。将 changeEmail.enabled 设置为 true

export const auth = betterAuth({
    user: {
        changeEmail: {
            enabled: true,
        }
    }
})

对于已验证邮箱的用户,提供 sendChangeEmailVerification 函数。此函数在用户更改邮箱时触发,会发送包含 URL 和令牌的验证邮件。如果当前邮箱未验证,更改将立即生效而无需验证。

export const auth = betterAuth({
    user: {
        changeEmail: {
            enabled: true,
            sendChangeEmailVerification: async ({ user, newEmail, url, token }, request) => {
                await sendEmail({
                    to: user.email, // 验证邮件必须发送到当前用户邮箱以批准更改
                    subject: '批准邮箱更改',
                    text: `点击链接批准更改:${url}`
                })
            }
        }
    }
})

启用后,在客户端使用 changeEmail 函数来更新用户的邮箱。用户在更改邮箱前必须验证其当前邮箱。

await authClient.changeEmail({
    newEmail: "new-email@email.com",
    callbackURL: "/dashboard", // 验证后重定向的地址
});

验证通过后,新邮箱将在用户表中更新,并向新地址发送确认邮件。

如果当前邮箱未验证,新邮箱将在无需验证步骤的情况下直接更新。

修改密码

用户的密码并不存储在用户表中,而是存储在账户表中。要修改用户密码,您可以使用以下方法之一:

POST
/change-password
const { data, error } = await authClient.changePassword({    newPassword: "newpassword1234", // required    currentPassword: "oldpassword1234", // required    revokeOtherSessions: true,});
属性描述类型
newPassword
要设置的新密码
string
currentPassword
当前用户密码
string
revokeOtherSessions?
设置为 true 时,该用户的所有其他活动会话将被作废
boolean

设置密码

如果用户是通过 OAuth 或其他提供商注册的,他们将没有密码或凭据账户。在这种情况下,您可以使用 setPassword 操作为用户设置密码。出于安全原因,此功能只能从服务器端调用。我们建议让用户通过"忘记密码"流程来为其账户设置密码。

await auth.api.setPassword({
    body: { newPassword: "password" },
    headers: // 包含用户会话令牌的请求头
});

删除用户

Better Auth 提供了一个实用工具来从数据库中硬删除用户。默认情况下此功能是禁用的,但您可以通过传递 enabled:true 轻松启用它。

export const auth = betterAuth({
    //...其他配置
    user: {
        deleteUser: { 
            enabled: true
        } 
    }
})

一旦启用,您可以调用 authClient.deleteUser 来永久删除数据库中的用户数据。

添加删除前的验证

为了增强安全性,您可能希望在删除用户账户前确认用户的意图。常见的做法是发送验证邮件。为此,Better Auth 提供了 sendDeleteAccountVerification 工具函数。 如果您设置了 OAuth 并且希望用户无需重新登录新会话即可删除账户,这一点尤其必要。

以下是设置方法:

export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            sendDeleteAccountVerification: async (
                {
                    user,   // 用户对象
                    url, // 自动生成的删除链接
                    token  // 验证令牌(可用于生成自定义 URL)
                },
                request  // 原始请求对象(可选)
            ) => {
                // 在此处添加您的邮件发送逻辑
                // 示例:sendEmail(data.user.email, "验证删除操作", data.url);
            },
        },
    },
});

回调验证的工作原理:

  • 回调 URLsendDeleteAccountVerification 中提供的 URL 是一个预生成的链接,访问该链接将删除用户数据。
delete-user.ts
await authClient.deleteUser({
    callbackURL: "/goodbye" // 可提供回调 URL 以便在删除后重定向
});
  • 身份验证检查:用户必须登录到他们试图删除的账户。 如果用户未登录,删除过程将失败。

如果您发送了自定义 URL,可以使用带有令牌的 deleteUser 方法来删除用户。

delete-user.ts
await authClient.deleteUser({
    token
});

身份验证要求

要删除用户,必须满足以下条件之一:

  1. 有效密码

如果用户设置了密码,可以通过提供密码来删除账户。

delete-user.ts
await authClient.deleteUser({
    password: "password"
});
  1. 新近会话

用户必须拥有一个"新近"的会话令牌,这意味着用户必须最近登录过。如果未提供密码,则会检查此项。

默认情况下 session.freshAge 设置为 60 * 60 * 24(1天)。你可以通过在 auth 配置中传递 session 对象来更改此值。如果设置为 0,则会禁用新近性检查。如果你没有使用邮箱验证来删除账户,建议不要禁用此检查。

delete-user.ts
await authClient.deleteUser();
  1. 启用邮箱验证(OAuth 用户需要)

由于 OAuth 用户没有密码,我们需要发送验证邮件来确认用户删除账户的意图。如果你已经添加了 sendDeleteAccountVerification 回调函数,只需调用 deleteUser 方法而无需提供其他信息。

delete-user.ts
await authClient.deleteUser();
  1. 如果你有自定义的删除账户页面,并通过 sendDeleteAccountVerification 回调发送了该 URL。 那么你需要使用令牌调用 deleteUser 方法来完成删除操作。
delete-user.ts
await authClient.deleteUser({
    token
});

回调函数

beforeDelete:该回调在用户被删除之前调用。您可以使用此回调在删除用户之前执行任何清理或额外检查。

auth.ts
export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            beforeDelete: async (user) => {
                // 在此处执行任何清理或额外检查
            },
        },
    },
});

您也可以抛出 APIError 来中断删除过程。

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

export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            beforeDelete: async (user, request) => {
                if (user.email.includes("admin")) {
                    throw new APIError("BAD_REQUEST", {
                        message: "无法删除管理员账户",
                    });
                }
            },
        },
    },
});

afterDelete:该回调在用户被删除之后调用。您可以使用此回调在用户删除后执行任何清理或额外操作。

auth.ts
export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            afterDelete: async (user, request) => {
                // 在此处执行任何清理或额外操作
            },
        },
    },
});

账户

Better Auth 支持多种身份验证方法。每种身份验证方法被称为一个提供者(provider)。例如,邮箱密码验证是一个提供者,Google 验证是一个提供者,等等。

当用户使用某个提供者登录时,系统会为该用户创建一个账户。该账户存储提供者返回的身份验证数据。这些数据包括访问令牌(access token)、刷新令牌(refresh token)以及提供者返回的其他信息。

账户表存储用户的身份验证数据,点击此处查看数据表结构

列出用户账户

要列出用户账户,你可以使用 client.user.listAccounts 方法。该方法将返回与用户关联的所有账户。

const accounts = await authClient.listAccounts();

令牌加密

Better Auth 默认不对令牌进行加密,这是有意为之的设计。我们希望您能完全掌控加密和解密的处理方式,而不是内置可能造成混淆或限制的行为。如果您需要存储加密的令牌(如 accessToken 或 refreshToken),可以使用 databaseHooks 在保存到数据库之前对它们进行加密。

export const auth = betterAuth({
    databaseHooks: {
        account: {
            create: {
                before(account, context) {
                    const withEncryptedTokens = { ...account };
                    if (account.accessToken) {
                        const encryptedAccessToken = encrypt(account.accessToken)  
                        withEncryptedTokens.accessToken = encryptedAccessToken;
                    }
                    if (account.refreshToken) {
                        const encryptedRefreshToken = encrypt(account.refreshToken); 
                        withEncryptedTokens.refreshToken = encryptedRefreshToken;
                    }
                    return {
                        data: resultAccount
                    }
                },
            }
        }
    }
})

之后每当您检索账户信息时,请确保在使用令牌之前先对其进行解密。

账户关联

账户关联功能允许用户将多种身份验证方式与单个账户关联。通过 Better Auth,如果身份提供商确认用户的邮箱已验证,用户可以将额外的社交登录或 OAuth 提供商连接到其现有账户。

如果禁用账户关联,则无论提供商或邮箱验证状态如何,都无法关联任何账户。

auth.ts
export const auth = betterAuth({
    account: {
        accountLinking: {
            enabled: true, 
        }
    },
});

强制关联

您可以指定一个"可信提供商"列表。当用户使用可信提供商登录时,即使提供商未确认邮箱验证状态,其账户也会被自动关联。请谨慎使用此功能,因为它可能会增加账户被接管的风险。

auth.ts
export const auth = betterAuth({
    account: {
        accountLinking: {
            enabled: true,
            trustedProviders: ["google", "github"]
        }
    },
});

手动关联账户

已登录的用户可以手动将其账户关联到其他社交提供商或基于凭据的账户。

  • 关联社交账户: 在客户端使用 linkSocial 方法将社交提供商关联到用户账户。

    await authClient.linkSocial({
        provider: "google", // 要关联的提供商
        callbackURL: "/callback" // 关联完成后的回调 URL
    });

    关联社交账户时,您还可以请求特定的权限范围(scopes),这些范围可以与初始认证时使用的范围不同:

    await authClient.linkSocial({
        provider: "google",
        callbackURL: "/callback",
        scopes: ["https://www.googleapis.com/auth/drive.readonly"] // 请求额外的权限范围
    });

    您也可以直接使用 ID 令牌来关联账户,而无需重定向到提供商的 OAuth 流程:

    await authClient.linkSocial({
        provider: "google",
        idToken: {
            token: "id_token_from_provider",
            nonce: "nonce_used_for_token", // 可选
            accessToken: "access_token", // 可选,某些提供商可能需要
            refreshToken: "refresh_token" // 可选
        }
    });

    当您已经拥有来自提供商的有效令牌时,这非常有用,例如:

    • 使用原生 SDK 登录后
    • 使用处理认证的移动应用时
    • 实现自定义 OAuth 流程时

    ID 令牌必须有效,并且提供商必须支持 ID 令牌验证。

    如果您希望用户能够使用与当前用户不同的电子邮件地址关联社交账户,或者如果您想使用不返回电子邮件地址的提供商,您需要在账户关联设置中启用此功能。

    auth.ts
    export const auth = betterAuth({
        account: {
            accountLinking: {
                allowDifferentEmails: true
            }
        },
    });

    如果您希望新关联的账户更新用户信息,您需要在账户关联设置中启用此功能。

    auth.ts
    export const auth = betterAuth({
        account: {
            accountLinking: {
                updateUserInfoOnLink: true
            }
        },
    });
  • 关联基于凭据的账户: 要关联基于凭据的账户(例如,电子邮件和密码),用户可以发起“忘记密码”流程,或者您可以在服务器上调用 setPassword 方法。

    await auth.api.setPassword({
        headers: /* 包含用户会话令牌的请求头 */,
        password: /* 新密码 */
    });

出于安全原因,无法从客户端调用 setPassword

账户解绑

您可以通过提供 providerId 来解绑用户账户。

await authClient.unlinkAccount({
    providerId: "google"
});

// 解绑特定账户
await authClient.unlinkAccount({
    providerId: "google",
    accountId: "123"
});

如果账户不存在,将会抛出错误。此外,如果用户仅有一个账户,解绑操作将会失败以防止账户锁定,除非将 allowUnlinkingAll 设置为 true

auth.ts
export const auth = betterAuth({
    account: {
        accountLinking: {
            allowUnlinkingAll: true
        }
    },
});

On this page