twoFactor Skill: Implementing TOTP/OTP Two-Factor Authentication in Better Auth

前言

生产环境里,邮箱加密码的单因素登录很难满足安全与合规要求。自己从零实现 2FA,通常要处理 TOTP 算法、OTP 下发、备用码、登录中间态 Cookie、可信设备与限流等多块逻辑,容易漏细节或写出脆弱流程。Better Auth 已通过 twoFactor 插件把这些能力收进一套配置;twoFactor Agent Skill 则把官方实践整理成可被编码助手直接引用的说明,减少 AI 生成不完整或错误的 2FA 代码。

这是什么

twoFactor 是 Better Auth skills 仓库 下的 Agent Skill(目录名 twoFactor,SKILL 元数据中的名称为 two-factor-authentication-best-practices)。它面向已在使用 Better Auth 的项目,说明如何配置服务端 twoFactor() 插件、客户端 twoFactorClient(),以及启用、登录校验、可信设备与关闭等完整生命周期。在 officialskills.sh 上归类为官方安全类 Skill,由 VoltAgent 团队维护索引。

核心功能

以下能力均来自官方 Skill 文档,对应 Better Auth twoFactor 插件行为:

  • TOTP:对接身份验证器应用;可配置位数(6 或 8)、周期(默认 30 秒);校验时接受当前周期前后各一个时间窗的码。
  • OTP:通过 sendOTP 自定义邮件或短信下发;可配置有效期、位数、单码最大尝试次数;库内 OTP 存储支持 plain、encrypted、hashed 或自定义加解密。
  • 备用码(Backup codes):启用 2FA 时自动生成,单次有效;支持密码确认后重新生成(旧码全部失效)。
  • 登录流程:密码登录后若需 2FA,响应中带 twoFactorRedirect: true,客户端跳转校验页;可用 TOTP、OTP 或备用码完成,成功后建立会话。
  • 可信设备:校验时传 trustDevice: true,默认信任 30 天(trustDeviceMaxAge),每次登录可刷新。
  • 安全相关:凭据校验后先移除会话、再发临时 2FA Cookie(默认 10 分钟);2FA 端点内置限流(10 秒内 3 次);TOTP 密钥与默认备用码加密存储;OTP 校验使用恒定时间比较。
  • 限制:仅支持凭据(邮箱/密码)账号启用 2FA;启用与关闭均需密码确认。

安装与启用

在支持 Agent Skills 的编码助手中,可用官方给出的安装命令(与 officialskills.sh 一致):

npx skills add https://github.com/better-auth/skills --skill better-auth-twoFactor

也可将 Skill 目录链接交给助手安装:https://github.com/better-auth/skills/tree/main/better-auth/twoFactor。

安装 Skill 只影响助手上下文,应用侧仍需在项目中接入 Better Auth 插件并迁移数据库。

服务端:在 betterAuth 配置中加入 twoFactor,并设置 issuer(显示在验证器应用中的发行方名称):

import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";

export const auth = betterAuth({
  appName: "My App",
  plugins: [
    twoFactor({
      issuer: "My App",
    }),
  ],
});

客户端:使用 twoFactorClient,在需要二次验证时跳转(示例为 /2fa):

import { createAuthClient } from "better-auth/client";
import { twoFactorClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  plugins: [
    twoFactorClient({
      onTwoFactorRedirect() {
        window.location.href = "/2fa";
      },
    }),
  ],
});

数据库:使用内置 adapter 时执行:

npx auth@latest migrate

若使用 Drizzle 或 Prisma,按项目惯例 generate 并 push schema。验证:用户表应出现 twoFactorSecret 字段。

Cursor、Codex CLI、Claude Code 等对 npx skills add 的支持方式因产品而异;未在官方资料中单独说明的差异此处不展开,以各工具当前 Skills 文档为准。

典型用法

为用户开启 2FA

需先校验密码;返回 totpURI(用于生成二维码)和 backupCodes。twoFactorEnabled 在首次 TOTP 校验成功后才为 true(可用 skipVerificationOnEnable: true 跳过,官方不推荐)。

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
  }
};

展示二维码(示例使用 react-qr-code):

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

邮件 OTP 作为第二因素

在服务端配置 otpOptions.sendOTP,例如发邮件:

twoFactor({
  otpOptions: {
    sendOTP: async ({ user, otp }, ctx) => {
      await sendEmail({
        to: user.email,
        subject: "Your verification code",
        text: `Your code is: ${otp}`,
      });
    },
    period: 5,
    digits: 6,
    allowedAttempts: 5,
    storeOTP: "encrypted",
  },
});

客户端发送与校验:authClient.twoFactor.sendOtp();authClient.twoFactor.verifyOtp({ code, trustDevice: true })。

登录时的 2FA 分支

  1. 调用 signIn.email({ email, password })
  2. 在 onSuccess 中检查 context.data.twoFactorRedirect
  3. 若为 true,进入 /2fa 等校验页
  4. 用 TOTP、OTP 或备用码完成校验
  5. 成功后写入会话 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";
        }
      },
    }
  );
};

服务端直连 API 时,可检查响应是否包含 twoFactorRedirect。备用码恢复:authClient.twoFactor.verifyBackupCode({ code, trustDevice: true })。关闭 2FA:authClient.twoFactor.disable({ password }),并会撤销可信设备记录。

更完整的 issuer、totpOptions、backupCodeOptions、twoFactorCookieMaxAge、trustDeviceMaxAge 等组合见官方 Skill 中的 Complete Configuration Example。

适用场景与注意

适合:已在用 Better Auth、需要 TOTP 验证器、邮件/短信 OTP、备用码与「信任本设备」登录体验的 Web 或全栈应用;希望助手按官方字段与流程生成配置和客户端调用,而不是手写一套 TOTP。

注意:

  • 2FA 仅适用于邮箱密码账号;OAuth 等需另做设计。
  • OTP 下发依赖你实现的 sendEmail 或短信通道,Skill 不包含具体邮件服务商集成。
  • 生产环境应使用加密存储 OTP/备用码,并理解默认限流与 Cookie 超时,按需调整 twoFactorCookieMaxAge、allowedAttempts 等。
  • Skill 描述的是插件能力;具体框架(Next.js、React 等)的路由与 UI 仍需自行实现。

结尾

twoFactor Skill 把 Better Auth 双因素认证的安装、客户端挂钩、TOTP/OTP/备用码与登录分支写成可检索的步骤与代码片段,适合在需要 MFA 时交给编码助手对照实现,避免单因素或半成品 2FA 流程。官方目录:https://github.com/better-auth/skills/tree/main/better-auth/twoFactor。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye