深度解析 Zoom Cobrowse SDK 会话生命周期:从 SDK 初始化、JWT 鉴权到 PIN 连接与状态追踪的完整实践指南
【免费下载链接】knowledge-work-pluginsOpen source repository of plugins primarily intended for knowledge workers to use in Claude Cowork项目地址: https://gitcode.com/GitHub_Trending/kn/knowledge-work-plugins
导读
本文围绕 Zoom Cobrowse SDK 的核心概念文档 session-lifecycle.md 展开,系统讲解一次浏览器协同浏览(Co-browsing)会话从创建到结束的完整生命周期:初始化两端 SDK → 生成角色化 JWT → 客户启动会话并获取 PIN → 客服凭 PIN 加入 → 通过会话事件追踪连接/断开/结束状态。读完本文,你将掌握客户与客服双角色架构下的会话编排方式、pincode_updated等关键事件的正确用法,以及断线重连、超时回收等边界行为,可直接指导你在客服支持场景中落地一套可运行的 Cobrowse 集成。
一、会话生命周期总览:一次 Cobrowse 会话的五个阶段
Zoom Cobrowse SDK 的会话生命周期可以用一条清晰的五步流水线概括:
- 初始化 SDK:在客户页面与客服页面分别加载并初始化
ZoomCobrowseSDK; - 生成角色化 JWT:服务端分别签发
role_type=1(客户)与role_type=2(客服)两类令牌; - 客户启动会话并获取 PIN:客户调用
session.start(),SDK 通过pincode_updated事件派发权威 PIN 码; - 客服凭 PIN 加入:客服在 Zoom 托管的 Desk iframe 中输入 PIN,服务端校验后签发客服令牌完成加入;
- 会话事件追踪状态:
session_started、agent_joined、agent_left、session_ended等事件驱动前后端 UI 同步状态。
在仓库的 SKILL.md 中,这一流程被描述为"大多数团队最先实现、也是演示中最符合预期的典型生产流程":客户先发起会话(role_type=1,后端创建会话记录并返回客户 JWT,SDK 启动后获得 PIN),客服随后加入(role_type=2,输入客户 PIN → 后端校验 PIN 与会话状态 → 返回客服 JWT → 加载 Zoom 托管 Desk iframe 或自定义客服 UI)。如果演示中只有一个笼统的"session 用户",对真实的 Cobrowse 运维而言是不完整的。
二、阶段一:在客户与客服页面初始化 SDK
会话两端需要不同的集成方式,但共用同一套 JWT 鉴权模式。仓库概念文档 two-roles-pattern.md 中的角色对照如下:
| 角色 | role_type | 集成方式 | 是否需 JWT | 用途 |
|---|---|---|---|---|
| 客户(Customer) | 1 | 网站集成(CDN 或 npm) | 是 | 共享自身浏览器会话的用户 |
| 客服(Agent) | 2 | Iframe(CDN)或 npm(仅 BYOP) | 是 | 查看并协助客户页面的支持人员 |
客户侧:通过 CDN 加载并初始化
客户侧通常以 CDN 方式引入 SDK,将加载脚本置于页面<head>中(完整示例见 get-started.md):
<script type="module"> const ZOOM_SDK_KEY = 'YOUR_SDK_KEY'; (function (r, a, b, f, c, d) { r[f] = r[f] || { init: function () { r.ZoomCobrowseSDKInitArgs = arguments; }, }; var fragment = a.createDocumentFragment(); function loadJs(url) { c = a.createElement(b); d = a.getElementsByTagName(b)[0]; c.async = false; c.src = url; fragment.appendChild(c); } loadJs( `https://us01-zcb.zoom.us/static/resource/sdk/${ZOOM_SDK_KEY}/js/2.13.2` ); d.parentNode.insertBefore(fragment, d); })(window, document, 'script', 'ZoomCobrowseSDK'); </script>需要说明的版本要点:CDN URL 中的版本号支持语义化版本策略——固定版本js/2.13.2(精确使用 2.13.2)或补丁通道js/2.13.x(取>=2.13.0 且 <2.14.0的最新补丁)。仓库记录当前版本为 2.13.2(截至 2026 年 2 月)。SDK Key 会直接出现在 CDN URL 中,因此它是公开凭据。
初始化时通过settings配置功能开关与隐私策略,并在回调中获得session对象:
const settings = { allowCustomerAnnotation: true, piiMask: { maskType: 'all_input' }, }; ZoomCobrowseSDK.init(settings, function ({ success, session, error }) { if (success) { console.log("SDK initialized successfully"); // session 对象自此可用 } else { console.error("SDK init failed:", error); } });从 SKILL.md 的初始化设置清单可知,settings还支持allowAgentAnnotation(客服可绘制)、remoteAssist(远程协助)、multiTabSessionPersistence(多标签页会话延续)等配置,这些能力会在会话运行期影响交互行为。
客服侧:Zoom 托管的 Agent Portal iframe
客服端不直接初始化浏览器端 SDK,而是通过嵌入 iframe 连接会话(这是 CDN 分发模式下的标准做法):
<iframe id="agent-iframe" width="1024" height="768" src="" allow="autoplay *; camera *; microphone *; display-capture *; geolocation *;" ></iframe> <script> async function connectAgent() { const response = await fetch("https://YOUR_TOKEN_SERVICE_BASE_URL", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ role: 2, userId: "agent_" + Date.now(), userName: "Support Agent" }) }); const { token } = await response.json(); const iframe = document.getElementById("agent-iframe"); iframe.src = `https://us01-zcb.zoom.us/sdkapi/zcb/frame-templates/desk?access_token=${token}`; } connectAgent(); </script>iframe 的allow属性必须包含autoplay *(媒体自动播放)、camera *(摄像头)、microphone *(麦克风)、display-capture *(屏幕采集)与geolocation *(定位),否则客服端音视频与画面捕获能力可能受限。
三、阶段二:生成角色化 JWT 令牌
会话的鉴权基础是两类 role 专用的 JWT。签名必须发生在服务端,以保护 SDK Secret;前端只负责通过 HTTP 请求向自己的令牌服务换取 token。仓库概念文档 jwt-authentication.md 给出的三条准则值得牢记:绝不把 SDK Secret 暴露给客户端、签发短期令牌、为客户与客服角色分别生成不同令牌。
JWT 结构与角色差异
两类 JWT 使用相同的头部:
{ "alg": "HS256", "typ": "JWT" }Payload 因角色而异——客户 JWT(role_type=1):
{ "user_id": "user1_customer", "app_key": "YOUR_SDK_KEY", "role_type": 1, "user_name": "customer", "exp": 1723103759, "iat": 1723102859 }客服 JWT(role_type=2):
{ "user_id": "user2_agent", "app_key": "YOUR_SDK_KEY", "role_type": 2, "user_name": "agent", "exp": 1723103759, "iat": 1723102859 }Payload 字段说明
| 字段 | 必填 | 说明 |
|---|---|---|
app_key | 是 | 你的 ZoomSDK Key(不是 API Key) |
role_type | 是 | 用户角色:1= 客户,2= 客服 |
iat | 是 | 令牌签发时间戳(epoch 秒) |
exp | 是 | 令牌过期时间戳(epoch 秒),最短 30 分钟、最长 48 小时 |
user_id | 是 | 可唯一标识的用户 ID |
user_name | 是 | 用户名(最多 80 字符) |
enable_byop | 可选 | 启用 Bring Your Own PIN:1= 启用,0或省略 = 不启用 |
签名算法为使用 SDK Secret 的 HMAC-SHA256(注意不是 API Secret):
HMACSHA256( base64UrlEncode(header) + '.' + base64UrlEncode(payload), ZOOM_SDK_SECRET );令牌服务与典型端点拆分
官方提供了可直接克隆的鉴权端点样例工程(cobrowsesdk-auth-endpoint-sample),以.env配置ZOOM_SDK_KEY、ZOOM_SDK_SECRET与PORT后启动即可。令牌请求与响应的协议约定为:
// POST https://YOUR_TOKEN_SERVICE_BASE_URL { "role": 1, // 1 = customer, 2 = agent "userId": "user123", "userName": "John Doe" } // Response { "token": "eyJhbGciOiJIUzI1NiIs..." }仓库概念文档 two-roles-pattern.md 还给出了更贴近生产的推荐端点拆分,把"签发令牌"与"会话生命周期动作"绑定:
POST /api/customer/start:创建会话记录 + 签发客户令牌 + 生成 PIN;POST /api/agent/connect:校验 PIN 后签发客服令牌;POST /api/session/revoke:结束会话;GET /api/session/list:运维可见性查询。
该文档同时建议服务端按顺序创建三类对象:客户会话记录(含session_id、生成的 PIN、active/revoked状态、过期时间戳)、客户令牌(role_type=1,供客户浏览器启动/分享会话)、客服令牌(role_type=2,在 PIN 校验通过后签发,用于加载 Desk iframe 或自定义客服 UI)。
四、阶段三:客户启动会话并获取 PIN(关键事件)
客户在获得 JWT 后调用session.start({ sdkToken: token })启动会话。此阶段最容易踩坑的地方在于PIN 的来源——仓库多个文档反复强调同一条"关键 PIN 规则":
客服应使用的 PIN 必须来自客户 SDK 事件
pincode_updated,不要展示或依赖后端/会话占位符中的临时 PIN 值。UI 中应只显示一个明确标注的值(例如Support PIN),并将同一个值传递给客服侧流程。
如果忽略这条规则,客服 Desk 常常会以Pincode is not found(错误码30308)失败(见 SKILL.md 的 Read This First 章节)。
典型客户侧实现如下(完整示例见 get-started.md 与 customer-integration.md):
let sessionRef = null; const settings = { allowAgentAnnotation: true, allowCustomerAnnotation: true, piiMask: { maskType: "custom_input", maskCssSelectors: ".sensitive-field" } }; ZoomCobrowseSDK.init(settings, function({ success, session, error }) { if (success) { sessionRef = session; // 监听权威 PIN session.on("pincode_updated", (payload) => { console.log("PIN Code:", payload.pincode); document.getElementById("pin-display").innerHTML = `<p><strong>Your PIN:</strong> ${payload.pincode}</p> <p>Share this with your support agent</p>`; }); } else { console.error("SDK init failed:", error); } }); // 点击按钮时启动会话 document.getElementById("cobrowse-btn").addEventListener("click", async () => { const response = await fetch("https://YOUR_TOKEN_SERVICE_BASE_URL", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ role: 1, userId: "customer_" + Date.now(), userName: "Customer" }) }); const { token } = await response.json(); sessionRef.start({ sdkToken: token }); });自动 PIN 与 BYOP 自定义 PIN 两条路径
- 自动生成 PIN 流程:客户点击"启动会话"→ Zoom 生成 6 位数字 PIN → 客户把 PIN 告诉客服 → 客服输入 PIN 连接。这是默认行为,无需额外配置。
- BYOP 自定义 PIN 流程:应用自己生成 1~10 位字符(字母/数字)的自定义 PIN → 通过
session.start({ customPinCode: 'MYPIN', sdkToken })传入 → 客服输入该自定义 PIN 连接。启用 BYOP 需要在 JWT 中设置"enable_byop": 1。
BYOP 的价值在于可与既有工单系统打通:直接用工单/案件 ID 作为 PIN、集成 npm 分发实现自定义客服 UI(需要说明的是,npm 集成模式下必须使用 BYOP)。完整指南见 byop-custom-pin.md。
五、阶段四:客服凭 PIN 加入会话
客服侧流程与客户侧对称:先向令牌服务请求role=2的 JWT,再把令牌拼进 Desk iframe 的 URL(https://us01-zcb.zoom.us/sdkapi/zcb/frame-templates/desk?access_token=${token}),随后在 iframe 中输入客户提供的 PIN 完成加入。加入成功的标志是会话事件session_joined触发(见 SKILL.md 的 Agent Flow 章节)。
在真实实现中,客服令牌的签发应当在 PIN 校验通过之后进行——由POST /api/agent/connect端点统一完成"校验 PIN + 校验会话状态 + 签发令牌"三件事,从而避免为无效 PIN 浪费令牌额度并保证会话安全。
六、阶段五:会话事件追踪 connected / disconnected / end 状态
会话生命周期的事件驱动模型是整条流水线的"神经中枢"。仓库 SKILL.md 与 session-events.md 汇总的事件清单如下:
| 事件 | 语义 | 对应生命周期阶段 |
|---|---|---|
pincode_updated | PIN 更新(权威 PIN 来源) | 会话启动后 |
session_started | 会话已启动 | start 成功 |
session_ended | 会话已结束 | 任一方结束或超时 |
agent_joined | 客服已加入 | 客服输入 PIN 连接成功 |
agent_left | 客服已离开 | 客服退出 |
session_error | 会话错误 | 任意失败节点 |
session_reconnecting | 正在重连 | 页面刷新/网络中断 |
remote_assist_started | 远程协助开始 | 客服获得页面控制权 |
remote_assist_stopped | 远程协助结束 | 停止协助 |
监听方式统一为session.on(eventName, callback),示例:
session.on("session_started", () => console.log("Session started")); session.on("agent_joined", () => console.log("Agent joined")); session.on("agent_left", () => console.log("Agent left")); session.on("session_ended", () => console.log("Session ended")); session.on("session_error", (error) => console.error("Session error:", error));从源码结构看,这些事件与 SKILL.md 中列出的 SDK 方法一一对应:ZoomCobrowseSDK.init()、session.start()、session.join()、session.end()、session.on()、session.getSessionInfo()。事件监听应覆盖连接建立、成员进出、错误、重连、结束五类场景,才能支撑前端 UI 的完整状态机(按钮禁用/启用、加载态、连接态提示)。
生命周期各阶段的完整时间线
结合 SKILL.md 的 Session Lifecycle 章节,客户与客服两侧的完整流程如下:
客户侧:加载 SDK →ZoomCobrowseSDK.init(settings, callback)→ 请求role_type=1令牌 →session.start({ sdkToken })→pincode_updated触发 → 客户分享 6 位 PIN →agent_joined触发 → 实时同步开始 →session.end()或客服离开结束会话。
客服侧:请求role_type=2令牌 → 加载 Zoom Desk iframe → 输入客户 PIN →session_joined触发 → 查看客户浏览器 → 使用标注/远程协助/缩放工具 → 点击"Leave Cobrowse"离开。
会话超时与限制边界
为保证会话生命周期可控,SDK 内置了明确的超时与限额行为(见 SKILL.md 的 Session Limits 与 Session Timeout Behavior 表格):
| 场景 | 数值 | 行为 |
|---|---|---|
| 客服等待客户加入 | 3 分钟 | 会话自动结束 |
| 页面刷新重连窗口 | 2 分钟 | 超时未重连则会话结束 |
| 重连尝试次数 | 最多 2 次 | 失败后会话结束 |
| 每会话客户数 | 1 | 超限报错 1012SESSION_CUSTOMER_COUNT_LIMIT |
| 每会话客服数 | 5 | 超限报错 1013SESSION_AGENT_COUNT_LIMIT |
| 单浏览器活跃会话数 | 1 | 报错 1004SESSION_COUNT_LIMIT |
| PIN 最大长度 | 10 字符 | 超限报错 1008SESSION_PIN_INVALID_FORMAT |
七、生命周期中的异常分支:断线重连与会话恢复
页面刷新或瞬时网络中断不会立刻终结会话。SKILL 文档中给出了基于session.getSessionInfo()的恢复判断范式(完整指南见 auto-reconnection.md):
ZoomCobrowseSDK.init(settings, function({ success, session, error }) { if (success) { const sessionInfo = session.getSessionInfo(); // 会话可恢复:自动重新加入上一个会话 if (sessionInfo.sessionStatus === 'session_recoverable') { session.join(); } else { // 否则启动新会话 session.start({ sdkToken }); } } });需要特别说明的重连前提:刷新重连依赖浏览器第三方 Cookie。Safari 开启"阻止跨站跟踪"、Chrome 开启"阻止第三方 Cookie"或浏览器隐私模式下,刷新重连可能失效。此外,生产环境必须使用 HTTPS(仅 loopback/本地开发主机允许 HTTP)。
八、生命周期落地实践中的关键注意事项
- PIN 唯一事实来源:只使用
pincode_updated事件派发的 PIN,前后端 UI 展示同一个值(标注为 Support PIN),不展示后端预创建记录的临时 PIN。这是最容易引发客服 Desk30308错误的根因。 - 凭据分级:SDK Key 公开(出现在 CDN URL 与 JWT
app_key);SDK Secret 只能用于服务端签名;API Key/API Secret 仅用于可选的 REST API 调用,均不得进入前端。常见错误是误把 API Key 填进 JWT 的app_key声明。 - 会话状态机对齐:把
session_started/agent_joined/agent_left/session_ended/session_error事件映射到 UI 状态,避免出现"客服已加入但界面仍显示等待"的竞态。 - 跨域 iframe 场景:若页面被嵌入跨域 iframe,需在 iframe 内也注入 SDK 加载脚本;同源 iframe 则无需额外处理。CSP 与 CORS 头需放行
*.zoom.us域(见 cors-csp.md)。 - 错误码快速定位:会话类错误集中在 1001~1017(含会话限额、PIN 格式、网络错误等),令牌错误为 2001
TOKEN_INVALID,服务级错误为 9999(详见 error-codes.md)。
九、进一步阅读
- get-started.md:从凭据申请到首个会话的完整六步上手指南;
- two-roles-pattern.md:双角色架构与推荐端点拆分;
- jwt-authentication.md:JWT 结构、字段与签名细节;
- session-events.md:会话事件驱动的 UI 同步模式;
- auto-reconnection.md:刷新与断线恢复实现;
- SKILL.md:覆盖概念、示例、参考与排障的完整文档索引。
【免费下载链接】knowledge-work-pluginsOpen source repository of plugins primarily intended for knowledge workers to use in Claude Cowork项目地址: https://gitcode.com/GitHub_Trending/kn/knowledge-work-plugins
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考