EasyStarter logoEasyStarter

认证系统

用户认证与授权

概述

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 中维护静态的全局角色矩阵,包含 useradmin 两种角色。每个账户只持有一个角色。角色描述的是权限,不是 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 哪些角色是管理员。当前项目只声明了 useradmin 两种角色。

配置初始管理员

ADMIN_EMAIL 应填写将要作为管理员登录的真实账户邮箱,例如:

ADMIN_EMAIL=admin@yourcompany.com

这不是发件地址或 supportEmail。它必须与登录账户的邮箱一致,而且该账户必须已完成邮箱验证。邮箱匹配不区分大小写,配置值的首尾空格也会被移除。

当前 ADMIN_EMAIL 只支持一个邮箱,不要填写逗号分隔的邮箱列表。初始管理员登录后,可以在用户管理页为其他账户分配 admin 角色。

本地开发时,复制示例文件并填写邮箱:

cp apps/server/.dev.vars.example apps/server/.dev.vars
apps/server/.dev.vars
ADMIN_EMAIL=admin@yourcompany.com

生产环境使用服务端 Secret:

cp apps/server/.env.production.example apps/server/.env.production
apps/server/.env.production
ADMIN_EMAIL=admin@yourcompany.com

填写完成后上传到 Cloudflare:

pnpm -F server secrets:bulk:production

让管理员角色生效

  1. 启动或重新部署 Server。
  2. 使用 ADMIN_EMAIL 对应的账户完成邮箱验证。
  3. 退出账户后重新登录,以创建新会话。
  4. Server 会在创建会话前把该账户的 role 更新为 admin

新账户和已存在的账户都可以通过这个流程获得管理员角色。已存在的账户会在下一次已验证登录时升级。

删除或更换 ADMIN_EMAIL 不会自动撤销旧管理员。请先把旧管理员的角色改回 user,再更换环境变量。只要旧邮箱仍在 ADMIN_EMAIL 中,该账户再次登录时就会被恢复为 admin

默认权限矩阵

权限用途useradmin
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.listusers.setRoleusers.banusers.unban),不要对服务器调用 authClient.admin.*

On this page