ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Better Auth 集成实战指南:TypeScript 认证框架的配置、会话、插件与真实工程接线解析

Better Auth 集成实战指南:TypeScript 认证框架的配置、会话、插件与真实工程接线解析 Better Auth 集成实战指南TypeScript 认证框架的配置、会话、插件与真实工程接线解析【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novuBetter Authbetter-auth是一个 TypeScript 优先、框架无关的全栈认证框架通过插件体系支持邮箱/密码、OAuth 社交登录、魔术链接magic link、Passkey、双因素认证等多种能力。本仓库在.cursor/skills/better-auth-best-practices/SKILL.md中收录了面向开发与 AI 编码代理的集成最佳实践速查技能。本文将这份技能文档作为核心骨架完整展开其配置项、CLI 命令、数据库适配、会话策略、钩子与插件体系并结合仓库中仪表盘dashboard与 API 服务的真实接线代码说明 Better Auth 如何被落地到一个生产级的多租户应用中帮助你从零到一完成集成、并理解背后的关键决策点。一、整体定位与核心概念Better Auth 的核心设计理念是认证能力由一组可组合的插件plugin提供而不是堆在核心模块里。邮箱密码登录、OAuth、组织多租户、双因素、SSO 等能力各自独立成插件按需启用即可获得对应的数据库表、API 端点与客户端方法。在深入研究任何细节前需要始终记住一个原则技能文档要求始终以官方文档与最新 API 为准always consult better-auth.com/docs for code examples and latest API。本文的所有条目均基于当前仓库收录的技能版本整理若未来升级应以官方文档、Options Reference 与LLMs.txt中的最新内容为准。官方还建议在启用新插件或修改配置后重新审视本技能中数据库、会话、钩子三节因为插件会改变数据模型与可用端点。二、快速参考环境变量、文件位置与 CLI 命令1. 环境变量环境变量作用生成方式BETTER_AUTH_SECRET会话/加密密钥最少 32 个字符openssl rand -base64 32BETTER_AUTH_URL服务的基础 URL如https://example.com—技能文档给出的铁律是只有在环境变量未设置时才在配置对象里手动填写baseURL/secret。也就是说环境变量优先于代码内配置这既便于不同环境dev/staging/prod共享同一份代码也避免了把密钥硬编码进仓库。2. 配置文件位置Better Auth CLI 会自动在以下目录寻找名为auth.ts的文件././lib./utils./src即./src下如果配置文件不在上述约定位置使用--config参数显式指定自定义路径。3. CLI 命令命令用途npx better-auth/clilatest migrate内建数据库适配器场景下直接执行 schema 迁移npx better-auth/clilatest generate为 Prisma / Drizzle 等 ORM 生成 schema 定义npx better-auth/cli mcp --cursor将 Better Auth 文档以 MCPModel Context Protocol形式接入 AI 编码工具重要提醒在新增或修改插件之后必须重新运行上述 CLI 命令。因为插件会带来新的数据表、字段和配置类型只有重新生成/迁移schema 与类型定义才能与实际运行的代码保持一致这也是第六节常见陷阱中第 2 条的来源。三、核心配置选项详解技能文档给出的核心配置选项速查表如下下表在保留原文全部字段的基础上补充了实践注记配置选项说明与注意点appName可选的展示名称用于界面与邮件等场景baseURL仅在未设置BETTER_AUTH_URL时才需要配置否则应从环境变量读取basePath默认/api/auth若想挂在站点根路径可设为/secret仅在未设置BETTER_AUTH_SECRET时才需要配置database大多数功能会话、账户等的必需品详见第四节数据库secondaryStorageRedis / KV 类存储用于会话与限流计数启用后会话默认写入这里而非数据库emailAndPassword形如{ enabled: true }开启后激活邮箱密码登录端点socialProviders社交登录提供方如{ google: { clientId, clientSecret }, ... }plugins插件数组按需组合认证能力trustedOriginsCSRF 白名单用于允许跨源认证请求需要特别说明的是basePath它决定了认证端点的 URL 前缀。生产中大型单体服务通常不会把认证挂在应用根路径上而是挂在某个统一 API 前缀下见第九节的 Novu 实践通过配置baseURL指向.../v1/better-auth完成路径收编。而trustedOrigins与安全一节的disableCSRFCheck/disableOriginCheck直接相关跨域部署、前后端分离架构下认证请求的Origin校验必须与白名单精确匹配。四、数据库直连、ORM 适配器与模型名陷阱1. 两种连接方式Better Auth 的database配置支持两种接入形态直接连接driver 实例直接传入pg.Pool、mysql2pool、better-sqlite3或bun:sqlite实例。适合 Node 服务直连数据库、不想引入 ORM 的场景。ORM 适配器推荐用于已有 ORM 栈从专用子路径导入例如better-auth/adapters/drizzlebetter-auth/adapters/prismabetter-auth/adapters/mongodb2. 关键陷阱模型名 ≠ 表名技能文档用加粗体强调了一个极易踩坑的点Better Auth 使用的是适配器模型名adapter model name而不是底层表名。举例如果 Prisma 中的 model 名为User、映射到的数据库表名为users那么在配置中引用该模型时应写modelName: user对应 Prisma 的模型引用名采用单数绝不能写成users那是表名。所有涉及用户、会话、账户、验证令牌等实体的modelName配置都应遵循模型名语义。这个约定贯穿user.modelName、account.modelName、session相关配置以及databaseHooks中的实体引用。五、会话管理存储优先级、无状态模式与 Cookie 缓存策略会话是认证框架的核心Better Auth 的会话存储遵循一条优先级链如果配置了secondaryStorage会话默认写入 secondary storage如 Redis而不是数据库如果希望同时持久化到数据库设置session.storeSessionInDatabase: true无数据库 配置cookieCache进入完全无状态模式——会话只存在于 Cookie 中服务端不保存任何会话记录。Cookie 缓存cookieCache三种策略策略说明特点compact默认Base64url HMAC体积最小适合 Cookie 尺寸受限场景jwt标准 JWT可读载荷可见但带签名jwe加密 JWT载荷不可读安全性最高关键会话选项session.expiresIn会话有效期默认 7 天session.updateAge会话刷新间隔——会话在该时间后刷新以延长寿命session.cookieCache.maxAgeCookie 缓存本身的存活时长session.cookieCache.version缓存版本号一旦变更会使所有已签发的会话 Cookie 失效可用于全站强制登出的核按钮。结合无状态模式需要注意无数据库时会话仅存于 Cookie登出依赖 Cookie 过期/失效见第六节陷阱 5。六、用户与账户配置用户Useruser.modelName指定用户实体对应的模型名见第四节模型名陷阱user.fields字段/列映射用于把框架要求的字段映射到 ORM 中实际命名的字段user.additionalFields为 user 表追加自定义业务字段user.changeEmail.enabled是否允许用户更换邮箱默认关闭user.deleteUser.enabled是否允许用户自助删除账号默认关闭。账户Accountaccount.modelName账户实体模型名account.accountLinking.enabled是否启用账户关联同一用户绑定多个 OAuth/邮箱身份account.storeAccountCookie为无状态 OAuth场景把账户信息写入 Cookie。注册字段约束启用邮箱密码注册时email与name两个字段为必填项。如果采用自建注册流程需要保证提交载荷中携带这两个字段否则注册会被拒绝。七、邮件流验证、注册/登录触发与密码重置Better Auth 默认不发送任何邮件所有邮件发送动作都必须由开发者显式实现为 handler。技能文档列出的三类邮件入口配置项作用emailVerification.sendVerificationEmail发送验证邮件的处理器。必须定义它邮箱验证功能才会生效emailVerification.sendOnSignUp注册成功后自动触发发送验证邮件emailVerification.sendOnSignIn登录时若未验证自动触发发送验证邮件emailAndPassword.sendResetPassword发送密码重置邮件的处理器也就是说sendVerificationEmail与sendResetPassword是发信函数的挂载点函数内部由你接入自己的邮件服务而sendOnSignUp/sendOnSignIn是何时自动发送的开关。在本仓库的登录页实现中可以看到对应客户端侧的方法调用链——登录失败且用户未验证时前端会调用authClient.sendVerificationEmail({ email, callbackURL })让用户重发验证邮件见 apps/dashboard/src/utils/better-auth/components/sign-in.tsx#L26-L43。八、安全与高级选项advanced区块内的选项选项说明useSecureCookies强制 Cookie 仅走 HTTPS生产环境必须开启disableCSRFCheck关闭 CSRF 校验——技能文档标注 ⚠️安全风险disableOriginCheck关闭来源Origin校验——技能文档标注 ⚠️安全风险crossSubDomainCookies.enabled允许多个子域共享 Cookie如app.example.com与api.example.comipAddress.ipAddressHeaders自定义 IP 请求头用于反代/网关场景下的真实 IP 识别否则取到的是代理地址database.generateId自定义 ID 生成策略可传函数或serial/uuid/false限流Rate limitingrateLimit.enabled总开关rateLimit.window限流时间窗口rateLimit.max窗口内最大请求数rateLimit.storage限流计数的存放位置取值memory | database | secondary-storage。合理选择rateLimit.storage值得注意memory仅适合单实例开发环境多实例部署时计数不共享应改用database或secondary-storage如 Redis。这也与第二节中secondaryStorage的定位相呼应——Redis/KV 同时承担会话与限流两类高频读写。九、Hooks在认证生命周期中插入逻辑端点级 Hookshooks.before/hooks.after端点级钩子用于在具体认证端点如 sign-in、sign-up、验证端点执行前/后介入。其形态是{ hooks: { before: [{ matcher: /sign-in/email, handler: createAuthMiddleware(async (ctx) { ... }) }], after: [{ matcher: /sign-in/email, handler: ... }], } }每个钩子项由{ matcher, handler }组成。handler 通过createAuthMiddleware创建在中间件上下文中可访问ctx.path当前请求路径ctx.context.returned仅在after钩子中可用是端点返回值ctx.context.session当前会话对象。数据库 HooksdatabaseHooks数据库钩子挂在实体写操作前后支持user、session、account三类实体形如databaseHooks.user.create.before/databaseHooks.user.create.afterdatabaseHooks.session.create.before/afterdatabaseHooks.account.create.before/after典型用途包括写入前填充默认值、写入后触发外部副作用审计、通知、同步到业务库等。Hook 上下文ctx.context可用的能力清单技能文档完整罗列了上下文对象中可直接调用的能力成员作用session当前会话数据secret配置的认证密钥authCookies认证相关 Cookie 的读写封装password.hash()/password.verify()密码哈希与校验自定义登录/迁移场景极有用adapter底层数据适配器直接读写任意模型internalAdapter框架内部适配器可操作 user/session/account/verification 等内部模型generateId()ID 生成器可访问自定义 ID 策略tables各模型的表/模型定义引用baseURL当前配置的基础 URL十、插件体系按需组合认证能力导入规范必须使用专用子路径为保证 tree-shaking按需打包生效插件必须从专用路径导入import { twoFactor } from better-auth/plugins/two-factor;禁止写成from better-auth/plugins整包导入会破坏 tree-shaking把全部插件打进产物。这是技能文档明确强调的规范。常用插件一览twoFactor双因素、organization组织/多租户、passkey、magicLink魔术链接、emailOtp邮箱验证码、username、phoneNumber、admin管理后台、apiKey、bearerBearer Token、jwt、multiSession多会话、sso企业单点登录、oauthProvider、oidcProvider把 Better Auth 自身作为 OAuth/OIDC 提供方、openAPI生成 OpenAPI 文档、genericOAuth通用 OAuth。客户端插件服务端插件与客户端插件是配套的客户端插件要放到createAuthClient({ plugins: [...] })中才能让authClient暴露对应的客户端方法如组织插件的organization.list()、SSO 插件的跳转与回调处理。仓库实战organization sso 插件本仓库的仪表盘客户端就是一个插件化组合的实例。在 apps/dashboard/src/utils/better-auth/client.ts 中import { ssoClient } from better-auth/sso/client; import { organizationClient } from better-auth/client/plugins; import { createAuthClient } from better-auth/react; const baseURL BETTER_AUTH_BASE_URL || API_HOSTNAME || http://localhost:3000; export const BETTER_AUTH_API_URL ${baseURL}/v1/better-auth; export const authClient createAuthClient({ baseURL: BETTER_AUTH_API_URL, plugins: [organizationClient(), ssoClient()], // ... });可以看到组织插件organizationClient与 SSO 插件ssoClient同时被挂到createAuthClient上随后页面层即可直接调用组织相关方法。在 apps/dashboard/src/utils/better-auth/index.tsx 中认证提供方通过authClient.useSession()维护会话并在存在sessionData?.session?.activeOrganizationId时调用authClient.organization.getFullOrganization(...)拉取当前组织及其成员角色、authClient.organization.list()罗列用户所属组织、authClient.organization.setActive(...)切换当前组织signOut()则负责退出并清理本地令牌。这正是插件 数据表 服务端端点 客户端方法三位一体模型的直观印证。十一、客户端接入框架绑定、会话 API 与令牌管理按框架选择导入路径Better Auth 客户端按使用场景提供多种入口better-auth/client原生 vanilla JS/TSbetter-auth/reactReactHooksbetter-auth/vueVuebetter-auth/svelteSveltebetter-auth/solidSolid。核心方法技能文档列出的关键 APIsignUp.email()邮箱注册signIn.email()邮箱登录signIn.social()社交账号登录signOut()登出useSession()React Hook 形态的会话订阅服务端组件可用getSession()getSession()主动获取会话revokeSession()/revokeSessions()吊销单个 / 全部会话。仓库实战从fetchOptions到双通道令牌机制Better Auth 默认使用 Cookie 承载会话但这在纯 API/移动端或需要显式携带访问令牌的场景下并不够用。仓库客户端给出了一个完整的增强示例export const authClient createAuthClient({ baseURL: BETTER_AUTH_API_URL, plugins: [organizationClient(), ssoClient()], fetchOptions: { credentials: include, auth: { type: Bearer, token: () localStorage.getItem(better-auth-session-token) || , }, onSuccess: (ctx) { const authToken ctx.response.headers.get(set-auth-token); if (authToken) { localStorage.setItem(better-auth-session-token, authToken); } }, }, });要点拆解baseURL指向服务端挂载路径BETTER_AUTH_API_URL实际为${baseURL}/v1/better-auth。这意味着服务端把 Better Auth 的默认basePath/api/auth收编到了自己 API 网关的/v1/better-auth前缀之下见第十二节客户端用同一 baseURL 对齐credentials: include携带跨域 Cookie与 CORS 的credentials: true配套Bearer 令牌双通道signIn.email()成功后登录组件把返回的data.token写入localStorage的better-auth-session-token见 sign-in.tsx#L66-L70所有后续请求通过fetchOptions.auth自动带上该令牌令牌刷新回写onSuccess拦截响应头set-auth-token一旦服务端签发新令牌就更新本地存储——这与服务端会话滑动续期session.updateAge机制前后呼应登出时除了authClient.signOut()还需清理本地令牌localStorage.removeItem(better-auth-session-token)代码见 index.tsx#L85-L89。此外仓库还通过role-permissions.ts把 Better Auth 组织成员角色MemberRoleEnum来自novu/shared映射为权限集PermissionsEnum用于 UI 层的Protect/has()权限门控——这是认证Better Auth之上再叠一层业务级 RBAC的常见模式。十二、真实工程接线Better Auth 如何挂在 /v1/better-auth技能文档讲述的是 Better Auth 的通用集成规范本仓库则展示了在大型单体 API 中安全接入 Better Auth的完整工程实践。1. API 网关认证路径与通用 body-parser 解耦Better Auth 端点依赖原始请求体与流式处理与 NestJS 默认的全局 JSON body-parserbodyParser.json({ verify: rawBodyBuffer })存在冲突。在 apps/api/src/bootstrap.ts#L134-L148 中服务为认证路由做了专门的放行app.use((req, res, next) { if (req.path.startsWith(/v1/better-auth)) { return next(); } return bodyParser.json({ verify: rawBodyBuffer })(req, res, next); }); app.use((req, res, next) { if (req.path.startsWith(/v1/better-auth)) { return next(); } return bodyParser.urlencoded({ extended: true, verify: rawBodyBuffer })(req, res, next); });2. CORS认证路由必须显式校验来源禁止通配由于认证涉及携带凭证的跨域 CookieCORS 规则必须特殊对待。在 apps/api/src/config/cors.config.ts#L47-L61 中function enableWildcard(req: Request): boolean { return ( (isDevelopmentEnvironment() || isWidgetRoute(req.url) || isInboxRoute(req.url) || isBlueprintRoute(req.url) || isWebChatRoute(req.url)) !isBetterAuthRoute(req.url) ); } // BetterAuth routes require explicit origin validation for credential-based requests function isBetterAuthRoute(url: string): boolean { return url.startsWith(/v1/better-auth); }即开发环境或其他路由可以放宽为通配 origin但/v1/better-auth路由始终走白名单精确校验ALLOWED_ORIGINS_REGEX来自FRONT_BASE_URL并且credentials: true。这正是技能文档中trustedOrigins/ Origin 校验在生产多域架构下的落地形态。3. 特性开关Clerk 与 Better Auth 双轨共存本仓库的仪表盘处于从 Clerk 向 Better Auth 迁移的过渡期VITE_EE_AUTH_PROVIDER用于选择clerk还是better-auth客户端同时实现了一套以ClerkProvider命名的兼容封装其内部实际驱动的是authClient并在window.Clerk上暴露session.getToken兼容层见 apps/dashboard/src/utils/better-auth/index.tsx#L502-L510。对应的服务端也在特性开关下条件注册 auth 模块——apps/api/src/app/auth/auth.module.ts#L2-L18 依据isClerkEnabled() || isBetterAuthEnabled()决定是否装配。启动配置在 apps/dashboard/src/config/index.ts#L7-L9 的EE_AUTH_PROVIDER读取逻辑、以及 apps/dashboard/src/config/index.ts#L42-L46 的BETTER_AUTH_BASE_URL来自VITE_BETTER_AUTH_BASE_URL回退到VITE_API_HOSTNAME、最终默认http://localhost:3000中可以看到完整取值链。这一整套接线完整对应了技能文档第三节baseURL仅在环境变量未设置时才显式配置与basePath的思想可作为在既有网关/NestJS/多环境体系内引入 Better Auth 的范本。十三、类型安全让认证类型贯穿全栈Better Auth 的另一个卖点是端到端类型推断。1. 服务端推断从服务端auth实例直接推导会话与用户类型type Session typeof auth.$Infer.Session; // 整个会话user session type SessionUser typeof auth.$Infer.Session.user; // 仅用户部分2. 前后端分离的跨项目推断当客户端与服务端位于不同的项目/代码库时无法直接import服务端auth实例。此时用泛型把类型传递过去// 服务端auth 实例推导出的类型被引用进客户端项目的 .d.ts 或共享类型文件 createAuthClienttypeof auth();通过createAuthClienttypeof auth()客户端方法签名、插件方法返回值与useSession()的数据结构都将和服务端完全对齐杜绝手工维护接口类型导致的漂移。3. 仓库中的实践佐证本仓库也遵循先推断、后导出的类型哲学客户端创建后直接导出export type AuthClient typeof authClient;见 client.ts#L28供组件与工具函数引用同时自定义的会话用户、组织类型BetterAuthUser、BetterAuthOrganization见 auth-context.ts#L4-L16通过类型定义把 Better Auth 数据显式收敛到业务层可用的形态。十四、常见陷阱清单速查技能文档在末尾汇总了 6 条高频坑本文按症状 → 规避展开#陷阱规避要点1模型名 vs 表名混淆配置中永远用 ORM 模型名如 Prisma 引用名user而不是数据库表名如users2插件 schema 过期每次新增/变更插件后必须重跑 CLIgenerate/migrate让表结构与类型同步3secondaryStorage 的默认抢占一旦定义secondaryStorage会话默认写入它而非数据库需要双写时显式开session.storeSessionInDatabase: true4Cookie 缓存的自定义字段缺失自定义会话字段不会被写进 cookieCache读取时总会回到存储重新拉取不要假设缓存里有全部字段5无状态模式的登出语义无数据库时会话只存在于 Cookie登出 等 Cookie 到期/主动失效可借助cookieCache.version强制全体失效6改邮箱流程的方向更换邮箱时会先给当前邮箱发送确认成功后再向新邮箱发送验证——两端邮件逻辑都要实现总结Better Auth 的价值在于把认证从散落的重复劳动收敛为一套类型安全的配置 可组合插件体系。其工程要点可概括为四句话环境变量优先BETTER_AUTH_SECRET与BETTER_AUTH_URL是第一公民配置对象只是兜底一切皆模型数据库连接与钩子都围绕 ORM 模型名工作不要被物理表名带偏会话是策略问题DB / secondary storage / 无状态 Cookie 三种模式按规模与形态选择Cookie 缓存选型要权衡体积、可读性与安全插件要按路径导入、客户端要配对挂载schema、端点、客户端方法三者同生共灭。如果你要在一套已有网关、多环境、多租户的工程里落地它本仓库在apps/api/v1/better-auth前缀 body-parser 放行 CORS 白名单与apps/dashboardcreateAuthClient插件组合 Bearer 双通道令牌 organization/sso 客户端方法两侧的接线代码是一份可直接参照的生产级样本而本文所述的完整配置语义、CLI 生命周期、钩子上下文与陷阱清单则来自仓库技能文档.cursor/skills/better-auth-best-practices/SKILL.md可作为随时查阅的速查手册。【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表