ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Colyseus 认证模块 @colyseus/auth 版本演进解析:从 0.17.7 到 0.18.4 的修复、加载模型与 OAuth 实战

2026/10/6 2:39:24 拓冰建站 浏览量
Colyseus 认证模块 @colyseus/auth 版本演进解析:从 0.17.7 到 0.18.4 的修复、加载模型与 OAuth 实战 后端游戏开发【免费下载链接】colyseus⚔ Multiplayer Framework for Node.js项目地址https://gitcode.com/gh_mirrors/co/colyseus点击查看免费下载colyseus/auth是 Colyseus 多人在线框架的官方认证工具包为 Node.js 游戏服务器提供邮箱/密码注册登录、匿名登录、JWT 签发校验、OAuth 第三方登录与邮箱验证/密码重置等完整能力。本文以 packages/auth/CHANGELOG.md 的版本记录为线索结合 packages/auth/src 下的源码实现与 packages/auth/test 测试用例逐版本剖析 0.17.7 → 0.18.4 之间的关键变更帮助读者理解每个修复背后的真实原因、代码位置与部署影响并掌握该模块在 Colyseus 0.18 中的完整接入方式。版本脉络总览七个版本记录了哪些关键变化colyseus/auth当前版本为0.18.4见 packages/auth/package.jsonCHANGELOG 记录了最近七个版本的变更按时间倒序为版本核心变更一句话影响0.18.4无 JWT 密钥时忽略浏览器携带的跨应用 token同源多应用不再互相锁死客户端0.18.3require()与import共用同一份 ESM 构建消除进程内双份包副本#9790.18.2bundled 资源路径通过import.meta.dirname解析修复打包后 HTML/资源查找失败0.17.9修复/login、/register中onFindUserByEmail返回null被Object.assign掩盖的问题#922未找到用户不再被误当成空对象0.17.8OAuth 期间动态自动探测origintwitch 等 provider 不再强制手动配置auth.oauth.defaults.origin0.17.7修复 Express 的 origin/backend_url 探测工具OAuth 回调地址在 Express 场景下更可靠下文按版本逐一深入。所有结论均可在仓库源码与测试中找到对应证据。0.17.7 与 0.17.8OAuth origin 从必须手动配置到自动探测变更内容0.17.7修复 Express 场景下 OAuth 的 origin / backend_url 探测工具0.17.8修复 OAuth 期间的动态 origin 探测。此前部分 provider如twitch要求为每个环境手动设置auth.oauth.defaults.origin现在 origin 可以自动探测。源码实现两个版本变更的核心都在 oauth-endpoints.ts 与 oauth.ts 中动态 origin 白名单oauth.defaults中新增了dynamic: [origin]允许 grant 中间件初始化后再按请求动态设置 origin这正是 0.17.8 修复的前提// packages/auth/src/oauth.ts export const oauth { defaults: { transport: session, state: true, response: [tokens, raw, profile], // Allow origin to be set dynamically per-request // (needed when origin is auto-detected after grant middleware is initialized) dynamic: [origin], } as GrantProvider { prefix: never }, providers: {} as { [providerId in OAuthProviderName]: OAuthProviderConfig }, ... };按请求头探测 originoauthEndpoints的 start 端点GET /auth/provider/:providerId在每次请求时用originFromContext从请求头推导协议与主机再回填auth.backend_url与oauth.defaults.origin// packages/auth/src/oauth-endpoints.ts function originFromContext(ctx: any): string { const proto ctx.getHeader(x-forwarded-proto) ?? (ctx.request?.url?.startsWith(https://) ? https : http); const host ctx.getHeader(host) ?? localhost; return ${proto}://${host}; } // start 端点内部 if (!auth.backend_url) { auth.backend_url originFromContext(ctx); } if (!oauth.defaults.origin) { oauth.defaults.origin auth.backend_url; }也就是说只要请求经过反向代理并正确传递x-forwarded-proto与host头或直接请求时带上了host头origin 就能自动匹配当前环境无需在每个部署环境开发、预发、生产手动写死。实战要点生产环境务必正确配置代理头origin 探测依赖x-forwarded-protoHTTPS 判定与host。若你使用 Nginx/Traefik 反代需要确保这两个头被透传否则回调地址可能生成http://localhost之类错误值。手动指定仍然可用如果你的架构无法依赖请求头例如签名校验、边缘函数仍可显式设置auth.backend_url或auth.oauth.defaults.origin代码会优先使用已设置的值if (!auth.backend_url)判断。cookie 会话的密钥来源OAuth 状态通过签名 cookie 传递resolveSecret()的优先级为opts.cookieSecret→SESSION_SECRET→JWT.settings.secret→JWT_SECRET全部缺失会直接抛错提示。示例项目在 app.config.ts 开头即设置了process.env.JWT_SECRET与process.env.SESSION_SECRET可作为最小配置参考。0.17.9onFindUserByEmail返回 null 时不再被掩盖变更内容修复/login与/register路由中onFindUserByEmail返回值的 null 处理。此前null会被Object.assign静默转成{}掩盖用户不存在这一事实PR #922感谢 JoaoCnh。源码实现该修复对应 endpoints.ts 中两个端点的写法变化。登录端点loginEndpoint现在先做空判断再展开避免把null变成空对象后误判// packages/auth/src/endpoints.ts — loginEndpoint 核心逻辑 const user: any Object.assign({}, await auth.settings.onFindUserByEmail(ctx.body.email)); if (user await Hash.verify(ctx.body.password, user.password)) { ... } throw new Error(invalid_credentials);注册端点registerEndpoint则先用existingUser做存在性检查只有确实存在时才抛出email_already_in_use并对onFindUserByEmail抛出的异常单独记录日志而不是直接吞掉// packages/auth/src/endpoints.ts — registerEndpoint 核心逻辑 let existingUser: any; try { existingUser await auth.settings.onFindUserByEmail(email); } catch (e: any) { logger.error(colyseus/auth, onFindUserByEmail exception: e.stack); } try { if (existingUser) { throw new Error(email_already_in_use); } ... } catch (e: any) { if (e instanceof APIError) { throw e; } logger.error(e); throw new APIError(401, { error: e.message }); }为什么不直接 404 区分邮箱不存在源码注释与实现说明了一个安全设计登录与忘记密码接口的 email 校验刻意使用宽松类型z.string()不做格式检查格式校验在 handler 内部进行并统一以401 invalid_credentials暴露避免攻击者通过400 还是 401差异探测哪些邮箱真实存在见 endpoints.ts 中credentialsBody与emailFormat的注释。forgotPasswordEndpoint同样在onForgotPassword未配置时返回通用成功形状的响应保持不可枚举账号的同一姿态。0.18.2bundled 资源路径改用import.meta.dirname变更内容打包后的资源HTML 模板、oauth_help_urls.json路径改为通过import.meta.dirname解析避免不同打包器/运行目录下相对路径失效。源码实现此前资源定位依赖的__dirname/process.cwd()在 ESM 打包场景并不可靠。现在 templates.ts 与 oauth-endpoints.ts 统一使用import.meta.dirname// packages/auth/src/templates.ts export const htmlTemplatePath [ path.join(process.cwd(), html), // 消费者目录优先可覆盖模板 path.join(import.meta.dirname, .., html), // 包内 bundled 模板兜底 ].find((filePath) existsSync(filePath));// packages/auth/src/oauth-endpoints.ts function readHelpUrls(): Recordstring, string { try { const p path.join(import.meta.dirname, .., oauth_help_urls.json); return JSON.parse(fs.readFileSync(p, utf-8)); } catch { return {}; } }实战要点模板覆盖约定readTemplate(name)首先查找process.cwd()/html下的同名文件找不到再回退到包内html目录。因此你可以把自己的address-confirmation-email.html、reset-password-email.html放到项目根目录html/文件夹即可在不改代码的情况下定制邮件/页面外观内置模板见 packages/auth/html。非 ENOENT 错误照常抛出readTemplate仅在文件缺失ENOENT时回退其他读取错误权限、IO会继续上抛避免掩盖真实故障。import.meta.dirname需要较新 Node该特性在 Node 20.11 可用本包engines声明为node 22.xpackage.json部署前请确认运行时版本。0.18.3require()与import共用同一份 ESM 构建变更内容require(colyseus/auth)现在解析到与import相同的 ESM 构建产物一个进程同时使用两种加载方式时不再加载两份副本issue #979。源码实现package.json的exports字段中import与require均指向构建产物并通过module-sync提供同步 ESM 入口// packages/auth/package.json exports: { .: { source: ./src/index.ts, types: ./build/index.d.ts, module-sync: ./build/index.mjs, import: ./build/index.mjs, require: ./build/index.cjs }, ... }为什么重要colyseus/auth在 index.ts 中通过副作用安装Room.onAuth默认行为见下文与 Room 的集成。如果require与import各加载一份包实例就会出现两份 auth 单例、两份 JWT 配置、auth.settings改了一处另一处看不到的隐蔽问题。0.18.3 之后这类双加载场景被统一配置与单例状态始终一致。0.18.4未配置 JWT 密钥时浏览器携带的外来 token 被安全忽略变更内容同源同一 origin部署了多个应用时浏览器会把 A 应用登录得到的 auth token 也带给 B 应用的请求。当 B 应用不使用认证未配置 JWT secret时现在会直接忽略该 token而不是拒绝请求——此前这会把这个浏览器锁死在 B 应用之外。源码实现该行为实现在 index.ts 的副作用导入中。它仅在Room.onAuth仍是框架默认实现时安装 JWT 解码版本自定义onAuth或先导入的自定义替换优先// packages/auth/src/index.ts节选 (Room as any).onAuth async function jwtDecodingOnAuth(token: string) { if (!token) { return true; } if (!JWT.settings.secret !process.env.JWT_SECRET) { if (!warnedTokenWithoutSecret) { warnedTokenWithoutSecret true; logger.warn(colyseus/auth: ignoring the clients auth token, since no JWT secret is configured ...); } return true; // 无密钥 → 当作无 token放行匿名流程 } try { const decoded await JWT.verifyany(token); const check JWT.settings.revocationCheck; if (check) { const ok await check(decoded); if (!ok) { return false; } } return decoded; } catch { return false; // 畸形/过期 token → AUTH_FAILED } };完整行为矩阵源码注释明确给出了四种输入对应的结果输入行为客户端影响无 token返回true无 auth payloadclient.auth保持 undefined匿名流程正常有效 token返回解码后的 payloadclient.auth即登录用户信息如client.auth.id畸形/过期 token返回false加入房间失败AUTH_FAILED任意 token 无 JWT 密钥返回true等同无 token不误伤未启用认证的应用0.18.4 修复点配套能力服务端撤销检查JWT.settings.revocationCheck允许在签名/有效期校验通过后追加服务端撤销判定返回false即拒绝。colyseus/database会注册一个默认实现比对 token 的tokenVersion声明与用户行记录使管理后台的Revoke sessions / Ban在玩家下次重连时立即生效未使用colyseus/database的应用可自行赋值接入自己的撤销源见 JWT.ts 中revocationCheck注释。深入colyseus/auth 的完整架构与接入指南包结构与核心导出packages/auth/src/index.ts 是公共 API 出口核心导出包括auth—— 配置单例auth.settings回调集合、auth.prefix默认/auth、auth.backend_url、auth.middleware、auth.routes()Express 适配器、auth.endpoints(...)better-call 端点映射、auth.oauthJWT—— 签发/校验/中间件与revocationCheckHash—— scrypt 密码哈希工具各端点工厂loginEndpoint、registerEndpoint、anonymousEndpoint、forgotPasswordEndpoint、resetPasswordGetEndpoint、resetPasswordPostEndpoint、confirmEmailEndpoint、userdataEndpoint——可脱离路由器单独调用做类型化契约测试。端点清单默认前缀/auth由endpoints()组装endpoints.ts路由方法说明/auth/loginPOST邮箱密码登录校验邮箱格式与密码哈希失败统一 401invalid_credentials/auth/registerPOST注册密码最短 6 位重复邮箱返回 401email_already_in_use/auth/anonymousPOST匿名注册未配置回调时生成generateId(21)匿名 ID/auth/userdataGET需要 Bearer token返回onParseToken解析出的用户数据/auth/forgot-passwordPOST生成 30 分钟有效的重置 token 并调用onForgotPassword发邮件/auth/reset-passwordGET/POSTGET 渲染重置表单页POST 校验 token 并调用onResetPassword/auth/confirm-emailGET校验 token 后调用onEmailConfirmed/auth/provider/:providerIdGETOAuth 登录发起302 跳转 provider/auth/provider/:providerId/callbackGETOAuth 回调验证后生成 JWT通过postMessage回传父窗口细节重置密码 token 与已使用标记分别依赖 JWTexpiresIn: 30m与 presence 键reset-password:token30 分钟过期防止 token 重放见resetPasswordPostEndpoint密码重置成功后重定向到/auth/reset-password?success...。密码哈希格式Hash.ts 的存储格式为algorithm$salt-hex$hash-hex例如scrypt$8f1f...$3d2a...每次make()都会生成 16 字节32 位 hex随机盐同密码不同用户产生不同哈希verify()从存储串解析算法与盐后重新计算用crypto.timingSafeEqual恒定时间比较兼容旧格式存储串不含$时视为旧的AUTH_SALT共享盐方案isLegacy()可检测保证存量用户继续可登录默认算法scryptHash.algorithmsha1作为可选算法保留。自定义回调一览auth.settings回调默认行为必填onFindUserByEmail抛not implemented错误是onRegisterWithEmailAndPassword抛not implemented错误是onRegisterAnonymously未配置时生成匿名用户否onSendEmailConfirmation/onEmailConfirmed未配置否onForgotPassword/onResetPassword未配置onForgotPassword缺失时日志报错并返回通用成功响应否onOAuthProviderCallback仅console.debug提示需持久化用户否onParseToken原样返回 JWT payload否onGenerateToken将完整 userdata 编码进 JWTJWT.sign否onHashPasswordscrypt 哈希Hash.make否onCheckBanned未配置否onCheckBanned返回BannedInfo{ reason?, until? }即拒绝登录并返回 403banned返回null/false/undefined则放行。该设计把凭证有效但被封禁403与凭证无效401区分开见 auth.ts 与 Endpoints.test.ts 中 403 断言。与 Room 的集成一次副作用导入即可在示例项目 MyRoom.ts 中可以看到只需要import colyseus/auth;副作用导入框架默认的Room.onAuth即被替换为 JWT 解码版本SDK 调用joinOrCreate(...)时携带的 token 会被自动校验解码结果成为client.auth如client.auth.id房间内无需任何样板代码即可拿到登录用户身份。自定义了static onAuth的房间子类不受影响。双模式挂载Express 与 better-callauth.routes()是一个薄适配层它调用auth.endpoints({ settings, prefix })生成 better-call 端点映射再用dualModeEndpoints包装为 Express 中间件auth.ts。因此Express 项目app.use(auth.routes())保持原有挂载方式better-call 项目createRouter({ ...auth.endpoints(...) })展开映射无 Express 依赖同一份 handler 逻辑endpoints.ts是唯一事实来源两种模式行为一致。示例项目 app.config.ts 展示了真实接入设置JWT_SECRET与SESSION_SECRET、auth.oauth.addProvider(discord, {...})、然后在createRouter({...})中展开端点。测试验证Endpoints.test.ts 覆盖了本 CHANGELOG 涉及的大多数行为登录封禁错误密码 401onCheckBanned命中返回 403banned/auth/userdata带合法 Bearer token 返回用户无 token 返回 401HTML 路由GET /auth/reset-password渲染含 token 的表单无效 token 重定向到?error有效 token 触发onResetPassword并重定向?successconfirm-email未配置onEmailConfirmed时返回 404配置后校验 token 并回调OAuth未配置 provider 时渲染缺失配置帮助页含auth.oauth.addProvider示例代码配置后GET /auth/provider/discord返回 302 跳转 discord.com 并设置grantcookieendpoints({ oauth: false })时该路由 404auth.settings支持挂载后热修改后续请求立即生效。这些测试可以直接以TS_NODE_PROJECT../../tsconfig/tsconfig.test.json mocha test/**.test.ts test/**/**.test.ts --exit --timeout 15000方式在 packages/auth 目录运行见 package.json 的test脚本。版本升级与部署注意事项综合七个版本的变更升级到 0.18.4 时建议关注Node 版本包声明node 22.x且 0.18.2 依赖import.meta.dirname请确认运行时满足环境变量最小集使用邮箱/密码认证需JWT_SECRET或设置JWT.settings.secret使用 OAuth 需额外SESSION_SECRET或传入cookieSecret代理透传请求头0.17.8 后 OAuth origin 自动探测依赖x-forwarded-proto与host反向代理务必透传模板定制在process.cwd()/html放置同名 HTML 即可覆盖内置邮件/页面模板内置模板位于 packages/auth/html双加载安全0.18.3 后require/import共用同一构建进程内不应再出现 auth 单例不一致无认证应用0.18.4 后未配置 JWT secret 的应用会忽略同源浏览器带来的外来 token无需任何额外处理存量密码兼容Hash对旧版AUTH_SALT共享盐格式仍有verify兼容路径升级过程中旧用户不会掉线可结合isLegacy()在下次登录时惰性升级为新格式。赞分享后端游戏开发【免费下载链接】colyseus⚔ Multiplayer Framework for Node.js项目地址https://gitcode.com/gh_mirrors/co/colyseus点击查看免费下载相关推荐Colyseus 负载测试工具 colyseus/loadtest从 0.17 到 0.18 的版本演进、Node 22 门槛与 CLI 实现解析Colyseus 负载测试工具 colyseus/loadtest从 0.17 到 0.18 的版本演进、Node 22 门槛与 CLI 实现解析 本篇技术后端游戏开发Colyseus uWebSockets Transport 变更日志深度解读从 0.17.14 到 0.18.4 的稳定性演进与配置指南Colyseus uWebSockets Transport 变更日志深度解读从 0.17.14 到 0.18.4 的稳定性演进与配置指南 导读 本文以 c后端游戏开发QuickRecorder 轻量录屏免费快速上手QuickRecorder 轻量录屏免费快速上手 做在线课程或录软件演示时系统声音和自己的讲解常常各录各的后期还得手动对轨。QuickRecorder 是桌面应用音视频屏幕录制上一篇Metabase Embedding SDK 之 SdkQuestionEntityPublicProps 详解四种互斥方式渲染嵌入问题下一篇Web-Dev-For-Beginners 银行应用示例项目用原生 HTML5/CSS/JavaScript 构建无框架单页应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考