ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Activepieces Managed Auth 深度解析:基于 JWT 的嵌入式认证与外部 Token 交换机制

Activepieces Managed Auth 深度解析:基于 JWT 的嵌入式认证与外部 Token 交换机制 Activepieces Managed Auth 深度解析:基于 JWT 的嵌入式认证与外部 Token 交换机制【免费下载链接】activepiecesAI Agents MCPs AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepiecesManaged Auth(托管认证)是 Activepieces 面向 SaaS 厂商提供的嵌入式认证能力:厂商在自己的产品中嵌入 Activepieces 流程构建器,由厂商后端用 RSA 私钥签发短期 JWT,换取 Activepieces 侧完整的AuthenticationResponse(含访问令牌),实现用户、项目、权限与并发限制全部由外部 Token 的 claims 自动供给。本文以仓库中 managed-auth.md 为骨架,结合服务端、前端与共享包的源码实现,完整讲解其架构、Token 协议、交换流程与安全边界,读完即可在自己的 SaaS 产品中接入这一认证链路。一、什么是 Managed Auth:把构建器嵌进你的产品在 Activepieces 中,Embedding 指 SaaS 厂商(下称厂商)将 Activepieces 的流程构建器嵌入自有产品页面,让终端用户无需注册 Activepieces 账号即可直接编排流程。难点在于:Activepieces 需要为每一位终端用户自动创建账号与项目,并严格控制其可见的 Pieces 范围与并发能力——这不能靠用户手动注册完成,必须由厂商后端在每次会话启动时代为认证。Managed Auth 正是为此设计的服务端到服务端认证协议:厂商后端持有一把 RSA私钥,用它签发一个短时 JWT(External Access Token);JWT 通过 Activepieces 的 Embed SDK 传递给服务端端点POST /v1/managed-authn/external-token;服务端用存储在 Activepieces 侧的公钥验签,再从 claims 中提取externalUserId、externalProjectId、角色、Piece 范围、并发池等信息;服务端自动完成用户/项目/权限的按需创建或复用,最终返回一个完整AuthenticationResponse(含 Activepieces 访问令牌),前端 SDK 据此以该用户身份进入构建器。值得注意的是,该功能受平台套餐platform.plan.embeddingEnabled门控,但门控位于 Signing Key 模块,而非 external-token 端点本身。从源码看,signing-key-module.ts 在注册/v1/signing-keys路由前挂载了platformMustHaveFeatureEnabled((platform) platform.plan.embeddingEnabled)钩子;而 managed-authn-controller.ts 中的端点配置为securityAccess.public()——其安全性完全由 JWT 签名本身保证(详见安全边界一节)。二、整体架构与一次完整交换的时序从 app.ts 可见,managedAuthnModule与signingKeyModule在ApEdition.CLOUD与ApEdition.ENTERPRISE两个版本中均被注册(分别为 app.ts 与 app.ts),服务端模块注册路径为:/v1/managed-authn— managed-authn-module.ts 注册控制器前缀;/v1/signing-keys— signing-key-module.ts 注册签名密钥 CRUD。一次完整交换的时序如下:厂商后端 Activepieces Server | | | 1. 用 RSA 私钥签发 JWT (short-lived) | |-------------------------------------| (不经过 AP,由厂商自行签发) | | | 2. Embed SDK 调用 POST /v1/managed-authn/external-token |-------------------------------------| externalTokenExtractor: | | - 按 header kid 找到 Signing Key | | - 用公钥 RS256 验签、解析 payload | | getOrCreateProject (按 externalProjectId) | | 可选:更新 displayName / upsert 并发池 | | applyProjectPieceAccess (无条件执行) | | getOrCreateUser (按 externalUserId 哈希邮箱) | | upsert 项目成员 (默认 EDITOR) | | 签发 7 天 AP 访问令牌 |-------------------------------------| 返回 AuthenticationResponse其中服务端的编排逻辑全部位于 managed-authn-service.ts 的externalToken方法,而 JWT 校验与 payload 解析位于 external-token-extractor.ts。三、三个关键领域概念3.1 Signing Key:RSA 密钥对公钥存储在 Activepieces 平台侧(仅保存公钥,服务端无法伪造外部 Token);私钥由厂商自己保管,用于签发 JWT;JWT 的 header 中kid字段 Signing Key ID,服务端据此从数据库解析对应的公钥。从 signing-key-generator.ts 可以看到服务端生成密钥对的方式:使用 Node.jscrypto.generateKeyPair,算法rsa、模长 4096 位,公钥与私钥均使用pkcs1编码的 PEM 格式。而 signing-key-service.ts 的add方法在保存时只入库公钥,privateKey仅在创建响应中一次性返回给厂商——这意味着私钥一旦丢失无法从 Activepieces 侧找回,只能删除重建。3.2 externalUserId:厂商用户 ID厂商自己的用户标识,用于在 Activepieces 中确定性映射用户。它不会以原始形式入库,而是与platformId一起哈希为确定性的身份邮箱:sha256(managed_platformId_externalUserId)对应源码为 managed-authn-service.ts 的generateEmailHash,哈希结果还会经过trim().toLowerCase()清洗以保证比较一致性。因此 Managed Auth 用户永远没有真实邮箱,其身份标识完全由externalId维系。3.3 externalProjectId:厂商项目 ID厂商的项目标识,通过项目的externalId字段映射到 Activepieces 项目。服务端按(platformId, externalProjectId)查找,不存在则创建一个TEAM 类型项目(owned by 平台 owner),见 managed-authn-service.ts。四、前置准备:创建 Signing Key在调用 external-token 端点之前,厂商必须先通过平台 API 创建一把签名密钥:# 创建签名密钥(需要平台级管理员权限,受 embeddingEnabled 套餐门控) POST /v1/signing-keys Content-Type: application/json { displayName: my-saas-production }响应中会包含privateKey(仅此一次):{ id: sk_xxxx, platformId: platform_xxxx, publicKey: -----BEGIN RSA PUBLIC KEY-----..., privateKey: -----BEGIN RSA PRIVATE KEY-----..., algorithm: RSA, displayName: my-saas-production }Signing Key 的完整 CRUD(增、查列表、按 id 查、删)由 signing-key-service.ts 提供,删除时按(platformId, id)双重限定,防止跨平台误删。五、服务端签发 JWT:Token Payload 的四个版本外部 Token 的 payload 采用 Zod schema 解析,由 external-token-extractor.ts 的externalTokenPayload()定义。z.union的排列顺序是v4 → v3 → v2(最具体优先),因为 v2 的 schema 会剥离未知键,若放在前面会吞掉 v3/v4 的版本特定字段。v1/v2(无version字段,legacy)v2 在 v1 基础上扩展了role、嵌套pieces对象、并发池字段:{ externalUserId: user_123, externalProjectId: project_456, firstName: Jane, lastName: Doe, role: EDITOR, pieces: { filterType: ALLOWED, tags: [tag-a] }, concurrencyPoolKey: pool-1, concurrencyPoolLimit: 10 }role:可选的平台自定义项目角色名,缺省回退到DefaultProjectRole.EDITOR(见 external-token-extractor.ts 的getProjectRole);pieces.filterType与pieces.tags:legacy 的 Piece 范围表达;concurrencyPoolKey/concurrencyPoolLimit:可选并发池配置,二者必须同时出现才会生效(见服务端判空逻辑 managed-authn-service.ts)。v3(version: v3)嵌套的pieces被拍平为顶层字段:{ version: v3, externalUserId: user_123, externalProjectId: project_456, firstName: Jane, lastName: Doe, piecesFilterType: ALLOWED, piecesTags: [tag-a] }v4(version: v4)pieceSet为必填,其值是 Piece Set 的key(即命名 Piece Set 的标识,不再是 tag 列表):{ version: v4, externalUserId: user_123, externalProjectId: project_456, firstName: Jane, lastName: Doe, pieceSet: my-named-piece-set-key }三个版本的解析分支见 external-token-extractor.ts 的extractPieces:v4 提取pieceSetKey;v3 提取piecesFilterTypepiecesTags;v1/v2 从嵌套pieces提取;均无则回退到PiecesFilterType.NONE。六、externalToken 交换流程:逐步骤源码解读POST /v1/managed-authn/external-token的请求体仅含一个字段externalAccessToken,由共享包中的 managed-authn-requests.ts 定义(注释还提醒:改动该结构需同步更新 embed-sdk,因其不能有依赖)。服务端 managed-authn-service.ts 的externalToken方法按以下步骤执行:步骤 1:校验外部 TokenexternalTokenExtractor.extract()(见 external-token-extractor.ts):先jwtUtils.decode取出 header,若缺少kid直接抛出INVALID_BEARER_TOKEN(signing key id not found in the header);按kid查库获取 Signing Key(含平台公钥);用RS256(常量JwtSignAlgorithm.RS256,见 external-token-extractor.ts)验签并解析 payload,不校验 issuer(传入issuer: null);解析出ExternalPrincipal,其中platformId取自Signing Key 所属平台,而不是 payload——这是防止跨平台冒用的关键设计。验签失败的错误消息会原样透传回调用方,便于厂商排查。步骤 2:Get or Create Project按(platformId, externalProjectId)查找项目(managed-authn-service.ts):命中则直接复用;未命中则读取平台,创建一个displayName externalProjectId、owner 为平台 owner、type ProjectType.TEAM且带externalId的项目。步骤 3:可选地更新显示名与并发池若 payload 含projectDisplayName,则更新项目显示名(managed-authn-service.ts);若concurrencyPoolKey与concurrencyPoolLimit同时非空,则 upsert 并发池并绑定到项目(managed-authn-service.ts)——这使厂商可以在外部 Token 中按项目指定并发上限。步骤 4:应用 Piece 访问范围(无条件执行)applyProjectPieceAccess在每次交换时都会无条件运行,此处没有managePiecesEnabled门控(managed-authn-service.ts)。其回退顺序为:显式pieceSetkey(v4)→ 按(platformId, key)查找命名 Piece Set,命中即assignProject;未命中 → 取legacypiecesTags的第一个 tag(仅当filterType ALLOWED,多 tag 场景未启用,每个 tag 对应一个 key tagName 的命名 Set);仍无匹配(或 targetKey 为 undefined)→ 回退到平台的Default 默认 Piece Set,且当指定了 key 却查不到时会输出 warn 日志:pieceSet key key not found — falling back to default。值得注意的是,项目套餐(project plan)不会被写入——外部 Token 只影响 Piece 集合与并发池,不改变项目的计费套餐。步骤 5:Get or Create UsergetOrCreateUser(managed-authn-service.ts)按(platformId, externalId)查找用户:存在则直接返回;不存在则先getOrCreateUserIdentity创建身份(邮箱 sha256(managed_platformId_externalUserId)哈希值,密码为随机生成、provider: JWT、verified: true,见 managed-authn-service.ts),再创建platformRole: MEMBER的用户。步骤 6:Upsert 成员关系并签发访问令牌以(projectId, userId, projectRoleName)upsert 项目成员,角色默认来自第 5 节解析的projectRole(缺省EDITOR);读取身份后调用accessTokenManager.generateToken,签发7 天(7 * 24 * 60 * 60秒)的 Activepieces 访问令牌(managed-authn-service.ts);返回完整AuthenticationResponse:包含id、platformRole、status、externalId、platformId、姓名、email(哈希邮箱)、trackEvents、newsLetter、verified、token与projectId(managed-authn-service.ts)。控制器在返回前还会上报USER_SIGNED_UP事件,source: managed(见 managed-authn-controller.ts),方便平台侧追踪嵌入式注册来源。七、前端与 Embed SDK 侧:拿到 Token 之后服务端返回的AuthenticationResponse由嵌入方前端接收。仓库中对应的 API 客户端是 managed-auth-api.ts 的managedAuthApi.generateApToken,它封装了对/v1/managed-authn/external-token的 POST 调用,并从activepieces/shared引入ManagedAuthnRequestBody与AuthenticationResponse类型。在嵌入路由中,该客户端被实际调用:见 embed/index.tsx(managedAuthApi.generateApToken({ ... }))。典型集成方式为:厂商后端签发外部 JWT(见第五节 payload 结构);厂商前端将 JWT 交给 Activepieces Embed SDK;SDK 调用POST /v1/managed-authn/external-token换取 AP 访问令牌;携带该令牌以对应projectId进入构建器,即完成了免注册嵌入式登录。八、安全边界与已知注意事项(Gotchas)端点是公开的:POST /v1/managed-authn/external-token配置为securityAccess.public(),没有附加 API Key 或会话校验——JWT 签名本身就是全部安全。因此厂商必须确保私钥安全保管,并签发短时Token。平台归属取自 Signing Key 而非 payload:platformId由kid解析出的签名密钥决定,外部用户无法通过篡改 claims 进入其他平台。外部用户没有真实邮箱:身份邮箱是sha256(managed_platformId_externalUserId)的确定性哈希,不具备密码找回等邮件能力;UserIdentityProvider.JWT也表明其身份来源。项目套餐不被写入:外部 Token 只控制 Piece Set 与并发池绑定,不修改项目 plan。v2 schema 会剥离未知键:z.union必须保持 v4 → v3 → v2 的从前往后顺序,否则 v3/v4 的version特定字段会被静默丢弃,导致解析错误。私钥只在创建时返回一次:signingKeyService.add只持久化公钥,privateKey 一旦丢失需删除重建。九、关键文件索引层次路径职责文档managed-auth.md本主题的核心知识笔记(路径已核实)服务端模块managed-authn-module.ts注册/v1/managed-authn前缀服务端控制器managed-authn-controller.ts唯一的POST /external-token路由服务端编排managed-authn-service.ts项目/用户/成员/Piece 范围/令牌签发JWT 校验external-token-extractor.tsRS256 验签、v1/v2/v3/v4 payload 解析签名密钥signing-key-module.tsembeddingEnabled套餐门控密钥生成signing-key-generator.ts4096 位 RSA、pkcs1 PEM共享契约managed-authn-requests.ts请求体结构(externalAccessToken)前端客户端managed-auth-api.tsgenerateApTokenAPI 封装注册入口app.tsCLOUD / ENTERPRISE 版本挂载模块十、总结Managed Auth 是 Activepieces 嵌入式能力的认证基石:以 RSA 签名 JWT 作为信任根,通过kid解析平台归属,用externalUserId/externalProjectId实现用户与项目的确定性幂等映射,再以 v1–v4 多版本 payload 逐步演进 Piece 范围控制(从嵌套pieces到命名 Piece Set 的pieceSetkey),最后签发 7 天访问令牌供 Embed SDK 使用。理解其 Token 版本、回退语义与端点公开、签名即安全的边界,是正确、安全地将其接入自有 SaaS 产品的关键。【免费下载链接】activepiecesAI Agents MCPs AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进