认证系统
IAM 项目使用 Better-Auth 提供完整的用户认证和授权功能。
Better-Auth 配置
Section titled “Better-Auth 配置”认证系统使用 Better-Auth,配置在 packages/auth/src/index.ts:
import { betterAuth } from 'better-auth'import { drizzleAdapter } from 'better-auth/adapters/drizzle'import { admin, jwt } from 'better-auth/plugins'import { oauthProvider } from '@better-auth/oauth-provider'import { redis } from '@IAM/redis'
export const auth = betterAuth({ database: drizzleAdapter(db, { provider: 'pg', schema: schema }), trustedOrigins: [env.CORS_ORIGIN], emailAndPassword: { enabled: true }, advanced: { defaultCookieAttributes: { sameSite: 'none', secure: true, httpOnly: true } }, plugins: [ admin(), jwt(), oauthProvider({ loginPage: '/sign-in', consentPage: '/consent' }) ], // 配置存储在 Redis 中以实现"一键踢下线" secondaryStorage: { get: async (key) => { return await redis.get(key) }, set: async (key, value, ttl) => { if (ttl) { await redis.set(key, value, { ex: ttl }) } else { await redis.set(key, value) } }, delete: async (key) => { await redis.del(key) } }})- database: 使用 Drizzle 适配器连接 PostgreSQL
- trustedOrigins: 允许的跨域来源
- emailAndPassword: 启用邮箱密码认证
- advanced: Cookie 安全配置
- plugins: 通过插件扩展能力
admin(): 提供后台管理与更多模型jwt(): JWT Token 支持oauthProvider(): OAuth 2.1 / OIDC Provider 功能,详见 OAuth Provider 指南
- secondaryStorage: 使用 Redis 存储会话数据,支持”一键踢下线”功能
Redis 会话存储
Section titled “Redis 会话存储”项目使用 Upstash Redis 作为 secondaryStorage,用于存储会话数据和组织信息。这提供了以下优势:
- 一键踢下线: 可以快速使所有用户会话失效
- 高性能: Redis 提供快速的读写性能
- 可扩展: 支持分布式部署
Redis 配置
Section titled “Redis 配置”确保在环境变量中配置了 Redis 连接信息:
KV_REST_API_URL=https://your-redis-instance.upstash.ioKV_REST_API_TOKEN=your-redis-tokenRedis 客户端在 packages/redis/src/index.ts 中配置,并在 packages/auth/src/index.ts 中使用。
启用认证插件后的数据库更新流程
Section titled “启用认证插件后的数据库更新流程”当你调整 Better Auth 的插件组合、用户附加字段或 OAuth/OIDC 配置时,需要同步更新数据库结构。
当前项目主要使用:
import { admin, jwt } from 'better-auth/plugins'
export const auth = betterAuth({ // ...database、trustedOrigins、emailAndPassword、advanced 等配置 plugins: [admin(), jwt(), oauthProvider(/* ... */)]})以上配置变化可能会影响认证相关表结构,因此需要按以下步骤更新数据库:
- 根据最新配置生成/更新 Drizzle Schema
在仓库根目录执行:
pnpm dlx @better-auth/cli@latest generate \ --config packages/auth/src/index.ts \ --output packages/db/src/schema/auth.ts--config:告诉 CLI 配置文件在packages/auth/src/index.ts。--output:将根据插件生成的最新 Schema 输出到packages/db/src/schema/auth.ts。- 运行前请在根目录的
.env中配置好以下环境变量:DATABASE_URLBETTER_AUTH_SECRETBETTER_AUTH_URLCORS_ORIGIN
- 生成数据库迁移文件
pnpm db:generate该命令会通过 Turbo 在 @IAM/db 包中执行 drizzle-kit generate,在 packages/db/src/migrations 目录下生成迁移文件并更新 meta/_journal.json。
- 应用迁移到数据库
pnpm db:migrate该命令会在 @IAM/db 包中执行 drizzle-kit migrate,真正把上述变更应用到 DATABASE_URL 所指向的数据库中。
提示:在开发环境中,如果你临时使用
pnpm db:push直接推送结构变更,且遇到终端里方向键或回车无法响应确认提示的情况,通常是因为根目录脚本会先进入turbo -F @IAM/db db:push的任务交互界面,而内部的drizzle-kit push又会继续弹出数据删除确认。两层交互叠加后,在 IDE 集成终端中偶尔会出现按键没有正确透传的问题。这时建议直接绕过
Turbo,执行包内命令:Terminal window pnpm --filter @IAM/db run db:push如果你已经明确确认要接受数据删除提示,也可以显式自动确认:
Terminal window pnpm --filter @IAM/db run db:push --force
--force会自动接受所有 data-loss 提示,只建议在你清楚知道会删除哪些表、字段或数据时使用。
小结:启用/调整 Better-Auth 插件后,一定要先用 CLI 重新生成 Schema,然后通过
db:generate/db:migrate更新数据库结构。
用户注册和登录
Section titled “用户注册和登录”在 React 组件中使用认证客户端:
import { authClient } from '@/lib/auth-client'
// 注册await authClient.signUp.email({ email: 'user@example.com', password: 'password123', name: 'User Name'})
// 登录await authClient.signIn.email({ email: 'user@example.com', password: 'password123'})
// 登出await authClient.signOut()认证路由在 apps/server/src/index.ts 中配置:
app.on(['POST', 'GET'], '/api/auth/*', (c) => auth.handler(c.req.raw))所有 /api/auth/* 路径的请求都会被 Better-Auth 处理。
Better-Auth 自动管理用户会话:
- HTTP-only cookies: 使用安全的 HTTP-only cookies 存储会话
- 跨域支持: 支持跨域请求(CORS 配置)
- 自动刷新: 自动处理会话刷新
获取当前会话
Section titled “获取当前会话”import { authClient } from '@/lib/auth-client'
const session = await authClient.getSession()if (session) { console.log('User:', session.user)}在 tRPC 上下文中:
export async function createContext({ context }: CreateContextOptions) { const session = await auth.api.getSession({ headers: context.req.raw.headers }) return { session }}保护路由和 API
Section titled “保护路由和 API”保护前端路由
Section titled “保护前端路由”在 Next.js 中使用服务器组件检查会话:
import { auth } from "@IAM/auth";import { redirect } from "next/navigation";import { headers } from "next/headers";
export default async function ProtectedPage() { const session = await auth.api.getSession({ headers: headers(), });
if (!session) { redirect("/login"); }
return <div>Protected Content</div>;}保护 tRPC 过程
Section titled “保护 tRPC 过程”使用 protectedProcedure 创建受保护的过程:
import { protectedProcedure } from '@IAM/api'
export const appRouter = router({ privateData: protectedProcedure.query(({ ctx }) => { // ctx.session 包含当前用户会话 return { message: 'This is private', user: ctx.session.user } })})检查用户权限
Section titled “检查用户权限”在受保护的过程中检查用户权限:
protectedProcedure.query(({ ctx }) => { if (!ctx.session) { throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Authentication required' }) }
// 使用 ctx.session.user 访问用户信息 return { userId: ctx.session.user.id }})- 用户在前端填写注册表单
- 调用
authClient.signUp.email() - Better-Auth 验证输入并创建用户
- 自动创建会话并设置 cookie
- 用户被重定向到受保护页面
- 用户在前端填写登录表单
- 调用
authClient.signIn.email() - Better-Auth 验证凭据
- 创建会话并设置 cookie
- 用户被重定向到受保护页面
- 用户点击登出按钮
- 调用
authClient.signOut() - Better-Auth 清除会话
- 用户被重定向到登录页面
安全最佳实践
Section titled “安全最佳实践”- 密码强度: 确保密码符合安全要求
- HTTPS: 在生产环境使用 HTTPS
- Cookie 安全: 使用 secure 和 httpOnly cookies
- CORS 配置: 正确配置允许的来源
- 会话过期: 设置合理的会话过期时间
前端 Auth Client 配置
Section titled “前端 Auth Client 配置”在 apps/web/src/lib/auth-client.ts 中配置认证客户端:
import { oauthProviderClient } from '@better-auth/oauth-provider/client'import { createAuthClient } from 'better-auth/react'import { adminClient } from 'better-auth/client/plugins'
export const authClient = createAuthClient({ baseURL: env.NEXT_PUBLIC_SERVER_URL, plugins: [adminClient(), oauthProviderClient()]})客户端插件说明
Section titled “客户端插件说明”adminClient(): 管理端 API(用户管理、会话管理等)oauthProviderClient(): OAuth Provider API(客户端管理、授权管理等)
OAuth 2.1 授权码流程
Section titled “OAuth 2.1 授权码流程”当 IAM 系统作为 OAuth Provider 时,授权码流程的核心链路如下:
- 第三方客户端将用户重定向到 IAM 的授权端点
- 若用户未登录,IAM 先引导用户完成本地登录,再恢复 OIDC 流程
- 用户确认授权(或自动跳过),IAM 签发
authorization_code - 第三方客户端用
code在后端换取access_token
完整流程详解(含时序图、每一步的实现细节和常见误区)请参阅 OAuth Provider 授权流程。
- 了解 OAuth Provider 配置
- 了解 API 开发
- 查看 数据库管理
- 学习 开发工作流