身份认证
配置 Better Auth:邮箱、OAuth 与会话。
身份认证已通过 Better Auth 端到端集成:邮箱/密码 (强制验证)、密码重置、GitHub 与 Google OAuth、会话,以及用于角色管理的 admin 插件。
提供的能力
- 邮箱/密码注册,且强制邮箱验证。
- 通过邮件重置密码。
- GitHub 与 Google OAuth,支持账户关联。
- 基于数据库的会话(
session表)。 - 带
role字段的 admin 插件,用于基于角色的访问控制。
重要文件
| 文件 | 作用 |
|---|---|
src/lib/auth.ts | Better Auth 服务端配置(适配器、提供方、插件、邮件钩子) |
src/lib/auth-client.ts | React 客户端(signIn、signUp、signOut、useSession) |
src/app/api/auth/[...all]/route.ts | catch-all 路由处理器(toNextJsHandler) |
src/proxy.ts | 针对受保护路径的基于 cookie 的重定向 |
src/components/auth/ | LoginForm、OAuthButtons 及相关 UI |
src/lib/db/schema/auth.ts | user、session、account、verification 表 |
服务端配置
src/lib/auth.ts 将 Better Auth 接入 Drizzle 适配器与 Resend:
emailAndPassword.enabled且requireEmailVerification: true—— 新用户必须先 验证才能登录。emailVerification.sendOnSignUp: true—— 注册时发送验证邮件,通过sendEmail(Resend)投递。sendResetPassword—— 密码重置邮件使用ForgotPassword模板。socialProviders.github与socialProviders.google—— 从环境读取各自的 client ID/secret。account.accountLinking.enabled: true—— 按邮箱将 OAuth 账户关联到既有用户。plugins: [admin()]—— 添加 admin 插件与role字段。
默认强制验证。若未配置 RESEND_API_KEY,验证邮件无法发送,新账户也无法登录。本地
测试时请配置 邮件,或在 src/lib/auth.ts 中放宽
requireEmailVerification。
路由处理器
所有认证端点由一个 catch-all 路由提供:
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_SECRET 与 GOOGLE_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.ts的useSession()在客户端组件中读取 当前会话。 -
服务端: 在路由处理器与服务端组件中以权威方式读取会话:
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。
