用户与账户

用户和账户管理。

除了认证用户外,Better Auth 还提供了一组管理用户的方法。这包括更新用户信息、更改密码等。

用户表存储用户的认证数据 点击此处查看模式

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

更新用户

更新用户信息

要更新用户信息,可以使用客户端提供的 updateUser 函数。updateUser 函数接收一个具有以下属性的对象:

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

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

更改电子邮件

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

auth.ts
import { betterAuth } from "better-auth";
import { sendEmail } from './email'; // 你的发送邮件函数

export const auth = betterAuth({
    user: {
        changeEmail: {
            enabled: true,
        }
    },
    emailVerification: {
        // 发送验证邮件所需
        sendVerificationEmail: async ({ user, url, token }) => {
            void sendEmail({
                to: user.email,
            })
        }
    }
})

避免等待邮件发送完成以防止时序攻击。在无服务器平台上,使用 waitUntil 或类似功能确保邮件被发送。

默认情况下,当用户请求更改邮箱时,验证邮件会发送到新的邮箱地址。 用户验证新邮箱后,邮箱才会被更新。

使用当前邮箱确认

为加强安全性,可以要求用户先通过其当前邮箱确认更改,然后再向新邮箱发送验证邮件。实现此功能,需要提供 sendChangeEmailConfirmation 函数。

auth.ts
import { betterAuth } from "better-auth";
import { sendEmail } from './email'; // 你的发送邮件函数

export const auth = betterAuth({
    user: {
        changeEmail: {
            enabled: true,
            sendChangeEmailConfirmation: async ({ user, newEmail, url, token }, request) => { 
                void sendEmail({
                    to: user.email, // 发送到当前邮箱
                    subject: '批准邮箱更改',
                    text: `点击链接批准将邮箱更改为 ${newEmail}: ${url}`
                })
            }
        }
    },
    // ...
})

无需验证即可更新

如果你允许用户立即更新邮箱(仅当当前邮箱未验证时),可以启用 updateEmailWithoutVerification

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

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

如果 updateEmailWithoutVerification 为假(默认),即使当前邮箱未验证,新邮箱未验证之前邮箱也不会被更新。

客户端用法

在客户端使用 changeEmail 函数启动流程。

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

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,});
Parameters
newPasswordstringrequired

要设置的新密码

currentPasswordstringrequired

当前用户密码

revokeOtherSessionsboolean

设置为真时,将使此用户的其他所有活跃会话失效

设置密码

如果用户通过 OAuth 或其他提供者注册,则不会有密码或凭证账户。这种情况下,可以使用 setPassword 操作为用户设置密码。出于安全考虑,此函数只能在服务器端调用。建议用户通过“忘记密码”流程来设置密码。

set-password.ts
import { auth } from "@/lib/auth"

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

验证密码

verifyPassword 函数允许验证用户当前密码。适用于执行敏感操作(如更新安全设置)前确认用户身份。此函数只能在服务器端调用。

verify-password.ts
import { auth } from "@/lib/auth"

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

对于无密码的 OAuth 用户,建议对敏感操作使用邮箱验证或新鲜会话检查替代。

删除用户

Better Auth 提供了一个工具可从数据库硬删除用户。默认禁用,但可通过传递 enabled:true 轻松启用。

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

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

启用后,可调用 authClient.deleteUser 永久删除数据库中的用户数据。

删除前添加验证

为增强安全,通常会确认用户删除账户的意愿。常用方法是发送验证邮件。Better Auth 提供 sendDeleteAccountVerification 工具用于此目的。 尤其适用于配置了 OAuth,又希望用户无需再次登录即可删除账户的场景。

配置示例如下:

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

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

回调验证工作原理:

  • 回调 URLsendDeleteAccountVerification 中提供的 URL 是访问后删除用户数据的预生成链接。
import { authClient } from "@/lib/auth-client"

await authClient.deleteUser({
    callbackURL: "/goodbye" // 可提供删除后跳转 URL
});
  • 认证检查:用户必须登录欲删除的账户,否则删除失败。

如果已发送自定义 URL,则可通过带 token 的 deleteUser 方法删除用户。

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

await authClient.deleteUser({
    token
});

认证要求

删除用户需满足以下条件之一:

  1. 有效密码

如用户有密码,提供密码即可删除账户。

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

await authClient.deleteUser({
    password: "password"
});
  1. 新鲜会话

用户需拥有“新鲜”会话令牌,意即最近登录过。若未提供密码,则检测该条件。

默认 session.freshAge 设为 60 * 60 * 24(1 天)。可通过 auth 配置传入 session 对象更改。若设为 0,则禁用新鲜度检查。建议未启用邮箱验证删除时不要禁用此检查。

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

await authClient.deleteUser();
  1. 启用邮箱验证(用于 OAuth 用户)

OAuth 用户无密码,需发送验证邮件以确认删除意愿。若已添加 sendDeleteAccountVerification 回调,调用 deleteUser 无需其他信息即可。

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

await authClient.deleteUser();
  1. 自定义删除账户页面

如发送了自定义删除链接(通过 sendDeleteAccountVerification 回调),需使用包含 token 的 deleteUser 方法完成删除。

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

await authClient.deleteUser({
    token
});

回调函数

beforeDelete:此回调在删除用户前调用。可用来执行清理或额外检查。

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

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
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            afterDelete: async (user, request) => {
                // 在这里执行清理或其他操作
            },
        },
    },
});

账户

Better Auth 支持多种认证方式。每种认证方式称为一个提供者。例如,邮箱密码认证是一个提供者,Google 认证也是一个提供者。

当用户使用提供者登录时,会为该用户创建一个账户,账户存储提供者返回的认证数据,包括访问令牌、刷新令牌等。

账户表存储用户的认证数据 点击此处查看模式

列出用户账户

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

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

const accounts = await authClient.listAccounts();

令牌加密

Better Auth 默认不加密令牌,这是有意为之。我们希望你完全控制加密和解密的方式,而不是内置可能混淆或限制的行为。如果你需要存储加密的令牌(例如 accessToken 或 refreshToken),可以使用数据库钩子(databaseHooks)在保存前加密。

import { betterAuth } from "better-auth";

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: withEncryptedTokens
                    }
                },
            }
        }
    }
})

然后每次取回账户时,记得先解密令牌再使用。

账户关联

账户关联在默认启用,允许用户将多种认证方式关联到同一账户。使用 Better Auth,用户可为现有账户连接额外的社交登录或 OAuth 提供者(前提是提供者确认用户邮箱已验证)。

若禁用账户关联,则不论提供者或邮箱验证状态如何,均无法关联账户。

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

export const auth = betterAuth({
    account: {
        accountLinking: {
            enabled: false, 
        }
    },
});

强制关联

你可以指定一组“受信任提供者”。当用户通过该类提供者登录时,即使提供者未确认邮箱验证状态,其账户也会自动关联。请慎用此功能,因可能增加账户被接管风险。

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

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

手动关联账户

已登录用户可手动将账户关联到额外的社交提供者或凭证账户。

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

    import { authClient } from "@/lib/auth-client"
    
    await authClient.linkSocial({
        provider: "google", // 要关联的提供者
        callbackURL: "/callback" // 关联完成后的回调 URL
    });

    你也可以在关联社交账户时请求特定权限范围,与初次认证时的权限不同:

    import { authClient } from "@/lib/auth-client"
    
    await authClient.linkSocial({
        provider: "google",
        callbackURL: "/callback",
        scopes: ["https://www.googleapis.com/auth/drive.readonly"] // 请求额外权限
    });

    还可以直接使用 ID 令牌关联账户,无需跳转到提供者的 OAuth 流程:

    import { authClient } from "@/lib/auth-client"
    
    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 令牌必须有效且提供者支持验证。

    如果希望用户可以用与账户邮箱不同的邮箱关联社交账户,或使用不返回邮箱的提供者,需要在账户关联设置中启用此功能。

    auth.ts
    import { betterAuth } from "better-auth";
    
    export const auth = betterAuth({
        account: {
            accountLinking: {
                allowDifferentEmails: true
            }
        },
    });

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

    auth.ts
    import { betterAuth } from "better-auth";
    
    export const auth = betterAuth({
        account: {
            accountLinking: {
                updateUserInfoOnLink: true
            }
        },
    });
  • 关联凭证账户: 要关联凭证账户(如邮箱密码),用户可发起“忘记密码”流程,或你可在服务器端调用 setPassword

    set-password.ts
    import { auth } from "@/lib/auth"
    
    await auth.api.setPassword({
      body: {
          newPassword: "new-password", // 必填
      },
      headers: await headers() // 包含用户会话令牌的请求头
    });

出于安全考虑,setPassword 不可在客户端调用。

账户解除关联

你可以通过提供 providerId 来解除用户账户的关联。

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

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

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

如果账户不存在,则会抛出错误。另外,如果用户只有一个账户,解除关联将被阻止以防止账户锁定(除非设置 allowUnlinkingAlltrue)。

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

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