认证系统
用户认证与授权
概述
EasyStarter 使用 Better Auth 进行认证,提供完整的用户管理解决方案。
支持的认证方式
| 方式 | 说明 |
|---|---|
| 邮箱/密码 | 传统注册方式,支持邮箱验证 |
| GitHub OAuth | 使用 GitHub 账号一键登录 |
| Google OAuth | 使用 Google 账号一键登录 |
配置
环境变量
# 必需
AUTH_SECRET=your-secret-key
# OAuth(可选)
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# 邮件(用于验证)
RESEND_API_KEY=your-resend-api-key前端使用
登录
import { authClient } from "@/lib/auth-client";
// 邮箱/密码登录
await authClient.signIn.email({
email: "user@example.com",
password: "password123",
});
// OAuth 登录
await authClient.signIn.social({ provider: "github" });
await authClient.signIn.social({ provider: "google" });登出
await authClient.signOut();获取当前用户
const { data: session } = authClient.useSession();
const user = session?.user;会话管理
Better Auth 自动管理会话:
- 基于 Cookie 的会话存储
- 自动刷新,防止过期
- 跨标签页同步
路由保护
使用认证中间件保护路由:
// 在路由组件中
import { createFileRoute, redirect } from "@tanstack/react-router";
export const Route = createFileRoute("/dashboard")({
beforeLoad: async ({ context }) => {
if (!context.user) {
throw redirect({ to: "/sign-in" });
}
},
});角色与权限
EasyStarter 在 @repo/app-config/rbac 中维护静态的全局角色矩阵,包含 user、admin 两种角色。每个账户只持有一个角色。角色描述的是权限,不是 Membership Tier(计费权益)。
RBAC 已经集成到认证、Web、Native 和服务端 oRPC 中,没有需要另外开启的 rbac.enabled 开关。接入时只需启用管理功能、配置管理员邮箱,再让对应账户完成已验证登录。
在 appConfig 中开启管理功能
修改 packages/app-config/src/app-config.ts 中的 appConfig.common:
common: {
admin: {
paidUsers: {
enabled: true,
},
userManagement: {
enabled: true,
},
},
auth: {
// 其他认证配置……
rbac: {
defaultRole: "user",
adminRoles: ["admin"],
},
},
}admin.userManagement.enabled 控制用户管理导航、页面和服务端 API。admin.paidUsers.enabled 控制付费用户管理页面和 API。关闭时,对应功能会返回 NOT_FOUND,而不是只隐藏菜单。
defaultRole 应保持为 "user",这样新账户默认没有管理权限。不要为了开启 RBAC 而把它改成 "admin",否则每个新账户都会成为管理员。
adminRoles: ["admin"] 告诉 Better Auth 哪些角色是管理员。当前项目只声明了 user 和 admin 两种角色。
配置初始管理员
ADMIN_EMAIL 应填写将要作为管理员登录的真实账户邮箱,例如:
ADMIN_EMAIL=admin@yourcompany.com这不是发件地址或 supportEmail。它必须与登录账户的邮箱一致,而且该账户必须已完成邮箱验证。邮箱匹配不区分大小写,配置值的首尾空格也会被移除。
当前 ADMIN_EMAIL 只支持一个邮箱,不要填写逗号分隔的邮箱列表。初始管理员登录后,可以在用户管理页为其他账户分配 admin 角色。
本地开发时,复制示例文件并填写邮箱:
cp apps/server/.dev.vars.example apps/server/.dev.varsADMIN_EMAIL=admin@yourcompany.com生产环境使用服务端 Secret:
cp apps/server/.env.production.example apps/server/.env.productionADMIN_EMAIL=admin@yourcompany.com填写完成后上传到 Cloudflare:
pnpm -F server secrets:bulk:production让管理员角色生效
- 启动或重新部署 Server。
- 使用
ADMIN_EMAIL对应的账户完成邮箱验证。 - 退出账户后重新登录,以创建新会话。
- Server 会在创建会话前把该账户的
role更新为admin。
新账户和已存在的账户都可以通过这个流程获得管理员角色。已存在的账户会在下一次已验证登录时升级。
删除或更换 ADMIN_EMAIL 不会自动撤销旧管理员。请先把旧管理员的角色改回 user,再更换环境变量。只要旧邮箱仍在 ADMIN_EMAIL 中,该账户再次登录时就会被恢复为 admin。
默认权限矩阵
| 权限 | 用途 | user | admin |
|---|---|---|---|
admin:access | 进入管理区域 | ✗ | ✓ |
user:list | 查看用户 | ✗ | ✓ |
user:set-role | 修改用户角色 | ✗ | ✓ |
user:ban | 封禁和解封用户 | ✗ | ✓ |
credits:adjust | 调整积分 | ✗ | ✓ |
membership:grant-trial | 赠送 Membership 试用 | ✗ | ✓ |
operation:list | 查看管理操作记录 | ✗ | ✓ |
用户管理
用户管理页直接读取并更新已配置数据库中的真实账号。只有具备对应 RBAC 权限的账号才能查看用户、修改角色或更新封禁状态。模板不会自动生成假用户。
为新接口添加权限保护
普通管理接口可以直接使用 adminProcedure:
import { adminProcedure } from "@/lib/orpc";
export const someAdminAction = adminProcedure.handler(async () => {
// 只有具备 admin:access 的账户可以执行
});需要更细的权限时,在受保护的 procedure 中使用 assertPermission:
import { assertPermission, protectedProcedure } from "@/lib/orpc";
export const adjustCredits = protectedProcedure.handler(async ({ context }) => {
assertPermission(context, "credits", "adjust");
// 业务逻辑
});Web 和 Native 可以使用 hasPermission(role, resource, action) 隐藏无权限的入口或跳转到禁止访问页。前端检查只用于优化体验,服务端仍必须独立检查权限。
关闭的 Better Auth admin HTTP 路由
admin 插件用于角色存储、封禁语义以及服务端 auth.api.* 调用。其 /api/auth/admin/* HTTP 面在 apps/server/src/index.ts 中被有意拒绝。管理操作请走 oRPC(users.list、users.setRole、users.ban、users.unban),不要对服务器调用 authClient.admin.*。