身份认证

配置 Better Auth:邮箱、OAuth 与会话。

身份认证已通过 Better Auth 端到端集成:邮箱/密码 (强制验证)、密码重置、GitHub 与 Google OAuth、会话,以及用于角色管理的 admin 插件。

提供的能力

  • 邮箱/密码注册,且强制邮箱验证
  • 通过邮件重置密码。
  • GitHub 与 Google OAuth,支持账户关联。
  • 基于数据库的会话(session 表)。
  • role 字段的 admin 插件,用于基于角色的访问控制。

重要文件

文件作用
src/lib/auth.tsBetter Auth 服务端配置(适配器、提供方、插件、邮件钩子)
src/lib/auth-client.tsReact 客户端(signInsignUpsignOutuseSession
src/app/api/auth/[...all]/route.tscatch-all 路由处理器(toNextJsHandler
src/proxy.ts针对受保护路径的基于 cookie 的重定向
src/components/auth/LoginFormOAuthButtons 及相关 UI
src/lib/db/schema/auth.tsusersessionaccountverification

服务端配置

src/lib/auth.ts 将 Better Auth 接入 Drizzle 适配器与 Resend:

  • emailAndPassword.enabledrequireEmailVerification: true —— 新用户必须先 验证才能登录。
  • emailVerification.sendOnSignUp: true —— 注册时发送验证邮件,通过 sendEmail (Resend)投递。
  • sendResetPassword —— 密码重置邮件使用 ForgotPassword 模板。
  • socialProviders.githubsocialProviders.google —— 从环境读取各自的 client ID/secret。
  • account.accountLinking.enabled: true —— 按邮箱将 OAuth 账户关联到既有用户。
  • plugins: [admin()] —— 添加 admin 插件与 role 字段。

默认强制验证。若未配置 RESEND_API_KEY,验证邮件无法发送,新账户也无法登录。本地 测试时请配置 邮件,或在 src/lib/auth.ts 中放宽 requireEmailVerification

路由处理器

所有认证端点由一个 catch-all 路由提供:

src/app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";

export const { GET, POST } = toNextJsHandler(auth);

认证页面

路径用途
/auth/login邮箱/密码 + OAuth 登录
/auth/register注册
/auth/register-success“请查收邮件”确认页
/auth/confirm邮箱验证落地页
/auth/forgot-password申请重置邮件
/auth/reset-password设置新密码

OAuth 提供方

src/lib/auth.ts 始终注册 GitHub 与 Google,OAuthButtons 为每个提供方渲染一个 按钮。凭据来自 GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRETGOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET —— 见 环境变量

注册提供方时,将回调 URL 设为 {BETTER_AUTH_URL}/api/auth/callback/{provider},例如 http://localhost:3000/api/auth/callback/github

若未配置某个提供方,请在 src/components/auth/OAuthButtons.tsx(以及 src/lib/auth.ts 中的提供方条目)移除或隐藏其按钮。凭据为空的按钮会在提供方 侧失败。

会话与角色

  • 客户端: 来自 src/lib/auth-client.tsuseSession() 在客户端组件中读取 当前会话。

  • 服务端: 在路由处理器与服务端组件中以权威方式读取会话:

    const session = await auth.api.getSession({ headers: await headers() });
  • 管理员: user.role 字段(默认 "user")控制管理员访问。admin 用户 API 会强制校验:

    src/app/api/admin/users/route.ts
    if (session.user.role !== "admin") {
      return NextResponse.json({ success: false, error: "Forbidden" }, { status: 403 });
    }

导航可见性不等于授权。 src/proxy.ts 仅检查是否存在会话 cookie 来重定向 受保护路径(/dashboard/admin/setting/account),并不校验会话或角色。 每个敏感的 API 处理器与服务端组件都必须自行调用 auth.api.getSession 并检查角色, 正如 admin 用户 API 所做的那样。隐藏菜单项只是 UX 细节,而非安全边界。

验证

  • /auth/login → 注册处创建新账户。
  • 收到并点击验证邮件(需要 Resend)。
  • 用已验证的账户登录。
  • 使用 GitHub 与 Google 登录(如已配置)。
  • 申请密码重置并设置新密码。
  • 登出。
  • 登出状态下访问 /dashboard → 携带 callbackUrl 被重定向到登录页。

生产注意事项

  • 设置稳定且保密的 BETTER_AUTH_SECRET(轮换会使会话失效)。
  • BETTER_AUTH_URL 设为规范的 HTTPS URL,并注册匹配的 OAuth 回调 URL。
  • 在每个受保护的 API 与变更操作中保留权威的会话/角色校验 —— 不要依赖 proxy.ts

相关页面

On this page