
Rocket.Chat 内部 JWT 签名包 rocket.chat/jwt源码实现、License v3 签发链路与版本演进解析【免费下载链接】Rocket.ChatThe Secure CommsOS™ for mission-critical operations项目地址: https://gitcode.com/GitHub_Trending/ro/Rocket.Chatrocket.chat/jwt是 Rocket.Chat 单体仓库monorepo中一个私有的、仅服务端使用的轻量 JWT 工具包它基于jose封装了 RS256 非对称签名的签发、验签与测试密钥生成能力。本文以该包的 CHANGELOG.md 为主线结合 源码、单元测试 及其核心消费方 License 库Enterprise Edition 包逐一拆解读完后你既能掌握该包三个 API 的完整用法与默认参数也能理清 Enterprise 许可证如何从 v2RSA 加密对象平滑迁移到 v3JWT 签名令牌、ABAC 能力为何依赖该包以及它随仓库工具链演进的完整版本史。包定位给谁用、解决什么问题Rocket.Chat 的根目录 package.json 使用 Yarn 与 Turborepo 管理多个 workspacepackages/jwt是其中规模最小的包之一。从其 package.json 可以确认它的核心属性包名rocket.chat/jwt当前版本0.2.1标记为private: true即不发布到公共 npm registry唯一运行时依赖为jose: ^4.15.9JavaScript 平台的 JOSE 标准库负责 PKCS8/SPKI 密钥导入、JWS 签名与验证等底层密码学操作提供buildtsc 编译到dist、test/testunitjest、typecheck、lint等标准脚本直接依赖 workspace 内的rocket.chat/jest-presets与rocket.chat/tsconfig与仓库的测试与 TS 编译体系对齐。从依赖关系上看rocket.chat/jwt唯一被声明为 workspace 依赖的包是 EE 侧 License 库 —— 见 ee/packages/license/package.json 中的rocket.chat/jwt: workspace:^。这正是理解该包价值的关键Rocket.Chat Enterprise 的许可证License与统计令牌都是以 JWT 形式分发的而该包承担了令牌的签发与验签这一安全底座职责并不面向普通消息收发链路。源码剖析三个极简 API 与一组安全默认值整个包的公开实现只有约 30 行集中在 src/index.ts共导出三个异步函数。sign用 PKCS8 私钥签发 JWTexport async function sign(keyObject: object, pkcs8: string, alg RS256) { const privateKey await importPKCS8(pkcs8, alg); const token await new SignJWT(keyObject as JWTPayload).setProtectedHeader({ alg, typ: JWT }).sign(privateKey); return token; }要点入参keyObject是任意可 JSON 序列化的对象会被整体放入 JWT 的payloadpkcs8是 PEM/DER 编码的PKCS8 私钥经jose.importPKCS8按alg导入为KeyLike算法默认值alg RS256RSA SHA-256同时写入受保护头部{ alg, typ: JWT }无显式exp/iat等声明因此产物是一个结构固定的、以业务对象为载荷的签名令牌。verify用 SPKI 公钥验签并取回载荷export async function verify(jwt: string, spki: string, alg RS256) { const publicKey await importSPKI(spki, alg); const { payload, protectedHeader } await jwtVerify(jwt, publicKey, {}); return [payload, protectedHeader]; }要点验签使用SPKISubjectPublicKeyInfo公钥与签发侧 PKCS8 私钥构成非对称密钥对jwtVerify内部自动校验签名完整性并返回[payload, protectedHeader]二元组前者即当初sign的原始业务对象后者可核对alg与typ是否匹配预期。getPairs仅供测试环境的密钥对生成器export async function getPairs(): Promise[string, string] { if (process.env.NODE_ENV ! test) { throw new Error(This function should only be used in tests); } const { publicKey, privateKey } await generateKeyPair(RS256); const spki await exportSPKI(publicKey); const pkcs8 await exportPKCS8(privateKey); return [spki, pkcs8]; }这是一个刻意限定运行环境的工具只有在NODE_ENV test时才会生成一对 RS256 密钥返回[SPKI 公钥, PKCS8 私钥]供测试代码构造自签自验的令牌。任何生产路径误用都会直接抛错This function should only be used in tests。用测试反推契约一份接近真实的 License v3 载荷单元测试 不仅是包本身正确性的证明还侧面展示了 License v3 载荷的完整结构。测试先通过jose的generateKeyPair(RS256)生成临时密钥对再调用sign与verify走通签发—验签闭环最终断言expect(protectedHeader).toEqual({ alg: RS256, typ: JWT }); expect(payload).toEqual(licenseV3);其中licenseV3载荷包含了information许可证编号、自动续期、试用标记、授权方与授权对象、法律文本、标签等、validation允许的服务器 URL、服务器版本、云端工作区 ID、有效期与统计上报要求、grantedModules授权模块清单例如auditing、ldap-enterprise、livechat-enterprise、voip-enterprise、device-management、federation以及第 20 项abac和limitsactiveUsers、guestUsers、roomsPerGuest、privateApps、marketplaceApps各自以{ max, behavior }阶梯式限量behavior取值如start_fair_policy、prevent_action、invalidate_license。这份载荷与 CHANGELOG 中 v0.1.0 与 v0.2.0 两个 Minor 版本的功能点一一呼应是理解下游用法的第一手样例。CHANGELOG 逐版本解读一次完整的功能演进史CHANGELOG.md 由 changesets 工具自动生成记录了包从诞生0.1.0到当前0.2.1的全部发布轨迹。逐条对照仓库代码可以还原出每次变更背后的业务含义。0.1.0 / 0.1.0-rc.0 —— 伴随 License 库与 v3 许可证格式落地两个版本对应同一条 Minor 记录并注明提交号5f81a0f3cbImplemented the License library, it is used to handle the functionality like expiration date, modules, limits, etc. Also added a version v3 of the license, which contains an extended list of features. v2 is still supported, since we convert it to v3 on the fly.这是包的首个正式版本即本包与 EE 侧 License 库处理到期时间、模块开关、用量上限等功能在同一次改动中引入。关键承诺是License 新增 v3 格式并携带更长的功能列表同时 v2 仍被支持——运行时会即时on the fly将 v2 转换升级为 v3 再处理。v2→v3 的转换实现位于 v2/convertToV3.ts输入旧的ILicenseV2输出version: 3.0的ILicenseV3旧字段被逐一映射url→validation.serverUrls[0]类型标为regexexpiry/trialEnd→visualExpiration或validPeriods[0].validUntilmaxActiveUsers/maxGuestUsers/maxRoomsPerGuest及apps.maxPrivateApps/apps.maxMarketplaceApps→ 对应limits条目且行为统一为prevent_action旧modules中的 bundle捆绑包会被展开成具体模块getBundleModules并补上outbound-messaging、teams-voip、contact-id-verification、hide-watermark等固定模块tag缺失时会从 bundle 反推出标签并着色getTagColor。这份转换逻辑正是 CHANGELOG 所称v2 is still supported, since we convert it to v3 on the fly的代码级注解。0.1.1 / 0.1.1-rc.0 ——rocket.chat/ui-kit并入主仓库对应记录正式版链接 PR #31138feat(uikit): Moverocket.chat/ui-kitpackage to the main monorepo本次为Patch补丁级别变更根因是把rocket.chat/ui-kit从外部迁移进当前 monorepoapps/meteor与ee/相关代码大量import ... from rocket.chat/ui-kit的路径随之调整。对rocket.chat/jwt而言这属于仓库结构调整带来的连锁重发布而非 API 变化。0.2.0 / 0.2.0-rc.0 —— 为私有频道与私有团队引入 ABAC对应记录PR #37091Adds Attribute Based Access Control (ABAC) for private channels private teams.这是包历史上第二次Minor 级别变更且与功能直接相关Rocket.Chat 需要为私有频道与私有团队的访问控制引入基于属性的访问控制ABAC支持。旁证来自仓库EE 侧存在独立的 ABAC 工具包rocket.chat/abac见 ee/packages/abac/package.json描述为 Rocket.Chat - Attribute Based Access Control (ABAC) support utilities在 jwt 单元测试 的示例 License v3 载荷里grantedModules明确包含{ module: abac }——即 ABAC 作为一项 Enterprise 授权模块出现需要由本包签发/验证的许可证令牌来开启。从源码结构可以推断ABAC 权限规则的授予与验权依赖 Enterprise 许可证对abac模块的授权而许可证令牌的签发与验签又落在rocket.chat/jwt之上——这正是 ABAC 变更需要连带升级本包 Minor 版本的原因。0.2.1 / 0.2.1-rc.0 —— ESLint 工具链升级对应记录PR #38989chore(eslint): Upgrades ESLint and its configuration属于工程化例行升级仓库将 ESLint 及其配置整体升级开发依赖中已使用eslint: ~9.39.5。对包的功能无影响仅保证在统一的新 lint 体系下通过检查。-rc.0后缀与发布节奏CHANGELOG 中每个正式版本前都有一个对应的-rc.0预发布版本这是changesets Turborepo 版本管理的标准产物monorepo 中相互依赖的包如rocket.chat/jwt与rocket.chat/license的 EE 版本会先以-rc.0候选版整体发布并验证再发布正式版在 EE 侧 License 库的 CHANGELOG 中即可看到rocket.chat/jwt以0.2.0 → 0.2.1等版本被联动引用的记录。阅读这类-rc.0条目时应将其视为对应正式版本变更的预发布副本内容一致。核心消费方拆解License 令牌的加密与解密全链路真正体现rocket.chat/jwt价值的是 EE 侧 token.ts。它负责企业许可证与统计令牌的加解密其中v3 走的就是本包的 JWT 通道解密运行时使用decrypt(encrypted: string)判断令牌是否以RCV3_前缀开头。若是则去掉前缀取 JWT 字符串调用verify(jwt, PUBLIC_LICENSE_KEY_V3)——这里的公钥PUBLIC_LICENSE_KEY_V3实际由内嵌的 v2 公钥PUBLIC_LICENSE_KEY_V2做 base64 解码得到形成一套只内置公钥、可在任意服务器离线验签的机制非RCV3_前缀的旧令牌则回退到crypto.publicDecrypt走 v2 的非对称解密流程v2 是直接 RSA 加密的 JSON 文本而非 JWT加密/签发仅测试环境encrypt(license: ILicenseV3)与encryptStatsToken(...)明确以process.env.NODE_ENV ! test抛错作为保护内部通过getPairs()获取临时密钥对再用sign(license, pkcs8)产出RCV3_JWT或纯 JWT 令牌——与包内getPairs的仅测试可用约定完全咬合统计令牌decryptStatsToken同样用verify SPKI 公钥完成解包并返回 JSON 字符串。由此形成清晰的职责划分真正的 RSA 密钥对生成在测试中即时完成生产环境只持有内置公钥用于验签私钥由签发方Rocket.Chat 云/离线授权工具保管二者通过rocket.chat/jwt的sign/verify实现安全互通。这也解释了为什么包内getPairs会如此刻意地拒绝非测试环境。开发者视角如何在仓库内验证与使用该包跑单元测试在仓库根目录执行yarn workspace rocket.chat/jwt test或在包目录下yarn testjest 会运行 jwt.spec.ts 完成生成密钥 → 签发 License v3 载荷 → 验签并断言 payload/header 一致的闭环测试同时也充当了 License v3 载荷结构的活文档类型与规范检查yarn workspace rocket.chat/jwt typecheck与lint分别对应tsc --noEmit与eslint .二次开发约束若其他 workspace 包需要签发/验签令牌应像ee/packages/license一样把rocket.chat/jwt声明为workspace:^依赖并沿用sign/verify的 RS256 默认算法与{ alg, typ: JWT }头部约定生产验签私钥绝不可硬编码在仓库内。小结CHANGELOG 之外的完整拼图版本变更类型核心内容仓库证据0.1.0MinorLicense 库落地新增 v3 格式并即时兼容 v2convertToV3.ts0.1.1Patchrocket.chat/ui-kit迁入主仓库PR #31138仓库目录结构调整0.2.0Minor为私有频道/团队引入 ABACPR #37091abac 包、测试载荷中的abac模块0.2.1Patch升级 ESLint 及配置PR #38989依赖eslint ~9.39.5如果把 CHANGELOG 比作发布日志那么 src/index.ts 与 token.ts 就是它的实现注脚sign负责把企业 License 变成可离线验签的 JWTverify负责用内置公钥还原载荷与头部getPairs负责把整套机制约束在测试闭环内。对企业版功能的读者而言理解rocket.chat/jwt的演进就等于理解 Rocket.Chat 许可证体系从 v2 加密对象走向 v3 JWT 标准、并向 ABAC 等精细化授权能力扩展的关键一步。【免费下载链接】Rocket.ChatThe Secure CommsOS™ for mission-critical operations项目地址: https://gitcode.com/GitHub_Trending/ro/Rocket.Chat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考