关于 two-factor-authentication-best-practices
面向使用 Better Auth 的开发者,介绍为账号加入双重验证的配置与实施。指南涵盖 TOTP、邮件或短信 OTP、备用码及可信设备,并说明相关安全设置。
主要功能
- 01TOTP 验证器配置
- 02邮件或短信 OTP
- 03备用码生成与恢复
- 04可信设备管理
- 05验证码存储与速率限制
适用场景
- 01为密码账号添加第二验证步骤
- 02通过备用码恢复账号访问
- 03配置 OTP 发送和存储方式
- 04管理可信设备的登录体验
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ better-auth/ skills --skill two-factor-authentication-best-practices安装后在 Claude Code 中输入/two-factor-authentication-best-practices确认已加载。
详细介绍
设置
- 在服务器配置中添加
twoFactor()插件,并设置issuer - 在客户端配置中添加
twoFactorClient()插件 - 运行
npx auth@latest migrate(内置适配器),或为 Drizzle/Prisma 生成并推送迁移 - 验证:检查用户表中是否存在
twoFactorSecret列
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";
export const auth = betterAuth({
appName: "My App",
plugins: [
twoFactor({
issuer: "My App",
}),
],
});
客户端设置
import { createAuthClient } from "better-auth/client";
import { twoFactorClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [
twoFactorClient({
onTwoFactorRedirect() {
window.location.href = "/2fa";
},
}),
],
});
为用户启用 2FA
需要验证密码。返回 TOTP URI(用于生成二维码)和备用码。
const enable2FA = async (password: string) => {
const { data, error } = await authClient.twoFactor.enable({
password,
});
if (data) {
// data.totpURI — generate a QR code from this
// data.backupCodes — display to user
}
};
在首次 TOTP 验证成功之前,twoFactorEnabled 不会设为 true。可通过 skipVerificationOnEnable: true 覆盖此行为(不推荐)。
TOTP(身份验证器应用)
显示二维码
import QRCode from "react-qr-code";
const TotpSetup = ({ totpURI }: { totpURI: string }) => {
return <QRCode value={totpURI} />;
};
验证 TOTP 代码
接受当前时间前后一个周期内的代码:
const verifyTotp = async (code: string) => {
const { data, error } = await authClient.twoFactor.verifyTotp({
code,
trustDevice: true,
});
};
TOTP 配置选项
twoFactor({
totpOptions: {
digits: 6, // 6 or 8 digits (default: 6)
period: 30, // Code validity period in seconds (default: 30)
},
});
OTP(电子邮件/短信)
配置 OTP 发送方式
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";
import { sendEmail } from "./email";
export const auth = betterAuth({
plugins: [
twoFactor({
otpOptions: {
sendOTP: async ({ user, otp }, ctx) => {
await sendEmail({
to: user.email,
subject: "Your verification code",
text: `Your code is: ${otp}`,
});
},
period: 5, // Code validity in minutes (default: 3)
digits: 6, // Number of digits (default: 6)
allowedAttempts: 5, // Max verification attempts (default: 5)
},
}),
],
});
发送和验证 OTP
发送:authClient.twoFactor.sendOtp()。验证:authClient.twoFactor.verifyOtp({ code, trustDevice: true })。
OTP 存储安全
配置 OTP 代码在数据库中的存储方式:
twoFactor({
otpOptions: {
storeOTP: "encrypted", // Options: "plain", "encrypted", "hashed"
},
});
自定义加密方式:
twoFactor({
otpOptions: {
storeOTP: {
encrypt: async (token) => myEncrypt(token),
decrypt: async (token) => myDecrypt(token),
},
},
});
备用码
启用 2FA 时自动生成。每个代码只能使用一次。
显示备用码
const BackupCodes = ({ codes }: { codes: string[] }) => {
return (
<div>
<p>Save these codes in a secure location:</p>
<ul>
{codes.map((code, i) => (
<li key={i}>{code}</li>
))}
</ul>
</div>
);
};
重新生成备用码
使所有先前的代码失效:
const regenerateBackupCodes = async (password: string) => {
const { data, error } = await authClient.twoFactor.generateBackupCodes({
password,
});
// data.backupCodes contains the new codes
};
使用备用码恢复访问
const verifyBackupCode = async (code: string) => {
const { data, error } = await authClient.twoFactor.verifyBackupCode({
code,
trustDevice: true,
});
};
备用码配置
twoFactor({
backupCodeOptions: {
amount: 10, // Number of codes to generate (default: 10)
length: 10, // Length of each code (default: 10)
storeBackupCodes: "encrypted", // Options: "plain", "encrypted"
},
});
处理登录时的 2FA
需要 2FA 时,响应中会包含 twoFactorRedirect: true:
登录流程
- 调用
signIn.email({ email, password }) - 在
onSuccess中检查context.data.twoFactorRedirect - 如果为
true,则跳转到/2fa验证页面 - 使用 TOTP、OTP 或备用码进行验证
- 验证成功后创建会话 Cookie
const signIn = async (email: string, password: string) => {
const { data, error } = await authClient.signIn.email(
{ email, password },
{
onSuccess(context) {
if (context.data.twoFactorRedirect) {
window.location.href = "/2fa";
}
},
}
);
};
服务器端:使用 auth.api.signInEmail 时,检查响应中是否包含 "twoFactorRedirect"。
受信任设备
验证时传入 trustDevice: true。默认信任时长为 30 天(trustDeviceMaxAge)。每次登录时都会刷新。
安全注意事项
会话管理
流程:凭据 → 移除会话 → 临时 2FA Cookie(默认 10 分钟)→ 验证 → 创建会话。
twoFactor({
twoFactorCookieMaxAge: 600, // 10 minutes in seconds (default)
});
速率限制
内置限制:所有 2FA 端点每 10 秒最多请求 3 次。OTP 还有额外的尝试次数限制:
twoFactor({
otpOptions: {
allowedAttempts: 5, // Max attempts per OTP code (default: 5)
},
});
静态数据加密
TOTP 密钥:使用 auth secret 加密。备用码:默认加密。OTP:可配置("plain"、"encrypted"、"hashed")。验证时使用恒定时间比较。
只有凭据(电子邮件/密码)账户才能启用 2FA。
禁用 2FA
需要确认密码。会撤销受信任设备记录:
const disable2FA = async (password: string) => {
const { data, error } = await authClient.twoFactor.disable({
password,
});
};
完整配置示例
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";
import { sendEmail } from "./email";
export const auth = betterAuth({
appName: "My App",
plugins: [
twoFactor({
// TOTP settings
issuer: "My App",
totpOptions: {
digits: 6,
period: 30,
},
// OTP settings
otpOptions: {
sendOTP: async ({ user, otp }) => {
await sendEmail({
to: user.email,
subject: "Your verification code",
text: `Your code is: ${otp}`,
});
},
period: 5,
allowedAttempts: 5,
storeOTP: "encrypted",
},
// Backup code settings
backupCodeOptions: {
amount: 10,
length: 10,
storeBackupCodes: "encrypted",
},
// Session settings
twoFactorCookieMaxAge: 600, // 10 minutes
trustDeviceMaxAge: 30 * 24 * 60 * 60, // 30 days
}),
],
});