快速开始
快速开始
用最少的代码跑通一次完整的「身份握手」——工作空间用户点开你的组织扩展应用后传递身份信息,让开发者的扩展应用后端能够正确识别当前用户身份,获取用户身份信息。
- Workspace-Extension-SDK 的 Github 地址为: https://github.com/GPTBOTS/Workspace-Extension-SDK
- 拉式登录见 04-拉式登录。
以下快速教程使用**推式(Push)**模式进行简单演示:
前置:拿到密钥
你需要先获取组织扩展应用的 HS256 签名密钥,用来完成组织扩展应用的身份握手。
- 让工作空间的 OWNER/ADMIN 进入 工作空间 → 空间管理 → 扩展应用,点「添加」
- 应用名称、应用图标、应用入口 URL(
app_home_url) - 鉴权模式选 workspace_account(需要传递身份)
- 提交后系统一次性明文展示
App Secret,请立刻复制保存——关闭后无法再次查看,只能轮换。
密钥形如
wext_+ 64 位十六进制(Tier 2)。只保存在你的后端(环境变量 / 密钥管理服务),绝不写进前端、git、日志。
第 1 步:后端 —— 校验令牌的接口
新建一个后端接口,接收前端 POST 过来的 wsa,用密钥校验,成功后建立你自己的会话。
// Node / Express 示例
import express from 'express';
import { verifyWsa, WsaVerificationError } from '@gptbots/workspace-extension-verify';
const app = express();
app.use(express.json());
app.post('/session/exchange', (req, res) => {
try {
const identity = verifyWsa(req.body.wsa, {
secret: process.env.EXTENSION_APP_SECRET, // 你的密钥(只在后端)
audience: 'app.example.com', // 你的应用 host,必须等于令牌 aud
});
// identity = { accountId, role, workspaceId, username?, email?, avatar?, appName?, issuedAt?, expiresAt? }
const sid = createYourSession(identity); // 换成你自己的 session
res.cookie('sid', sid, { httpOnly: true, secure: true, sameSite: 'lax' });
res.json(identity);
} catch (e) {
const code = e instanceof WsaVerificationError ? e.code : 'Error';
res.status(401).json({ code }); // 校验失败一律拒绝
}
});
// Node / Express 示例
import express from 'express';
import { verifyWsa, WsaVerificationError } from '@gptbots/workspace-extension-verify';
const app = express();
app.use(express.json());
app.post('/session/exchange', (req, res) => {
try {
const identity = verifyWsa(req.body.wsa, {
secret: process.env.EXTENSION_APP_SECRET, // 你的密钥(只在后端)
audience: 'app.example.com', // 你的应用 host,必须等于令牌 aud
});
// identity = { accountId, role, workspaceId, username?, email?, avatar?, appName?, issuedAt?, expiresAt? }
const sid = createYourSession(identity); // 换成你自己的 session
res.cookie('sid', sid, { httpOnly: true, secure: true, sameSite: 'lax' });
res.json(identity);
} catch (e) {
const code = e instanceof WsaVerificationError ? e.code : 'Error';
res.status(401).json({ code }); // 校验失败一律拒绝
}
});
此代码块在浮窗中显示
verifyWsa会按顺序校验:签名 →iss→aud→exp(含iat/nbf)→ 必填声明。任一不过抛WsaVerificationError。详见 06-SDK-API-参考。
第 2 步:前端 —— 落地页消费令牌
用户被平台带到你的 app_home_url?wsa=<JWT>。在这个落地页上,读取 wsa、POST 给你自己的后端、然后从 URL 抹掉它。
// 你的落地页
import { consumeHandoff } from '@gptbots/workspace-extension-sdk';
const identity = await consumeHandoff({ exchangeUrl: '/session/exchange' });
// consumeHandoff 依次做了:
// 1) 读取当前 URL 的 ?wsa=
// 2) POST { wsa } 到 /session/exchange(你的后端在这里 verifyWsa)
// 3) 成功后用 history.replaceState 抹掉 URL 里的 ?wsa=
// 4) 返回你后端回传的 identity
console.log('当前工作空间用户:', identity.username, identity.role);
if (identity.role === 'MEMBER') hideAdminUI();
// 你的落地页
import { consumeHandoff } from '@gptbots/workspace-extension-sdk';
const identity = await consumeHandoff({ exchangeUrl: '/session/exchange' });
// consumeHandoff 依次做了:
// 1) 读取当前 URL 的 ?wsa=
// 2) POST { wsa } 到 /session/exchange(你的后端在这里 verifyWsa)
// 3) 成功后用 history.replaceState 抹掉 URL 里的 ?wsa=
// 4) 返回你后端回传的 identity
console.log('当前工作空间用户:', identity.username, identity.role);
if (identity.role === 'MEMBER') hideAdminUI();
此代码块在浮窗中显示
就这样,两段代码就完成了一次安全的身份握手。
完整链路一图流
工作空间「扩展」页 GPTBots 平台 你的应用
──────────────── ─────────── ────────
用户点击你的应用图标 ───────────────────▶ 校验点击人是该工作空间成员
用密钥签一枚 5 分钟有效的 wsa (JWT)
打开 https://app.example.com/?wsa=<JWT> ────────────────────────────▶ 落地页
consumeHandoff()
◀── POST /session/exchange { wsa } ──
你的后端 verifyWsa() → identity
建立自有 session
抹掉 URL 里的 ?wsa=
工作空间「扩展」页 GPTBots 平台 你的应用
──────────────── ─────────── ────────
用户点击你的应用图标 ───────────────────▶ 校验点击人是该工作空间成员
用密钥签一枚 5 分钟有效的 wsa (JWT)
打开 https://app.example.com/?wsa=<JWT> ────────────────────────────▶ 落地页
consumeHandoff()
◀── POST /session/exchange { wsa } ──
你的后端 verifyWsa() → identity
建立自有 session
抹掉 URL 里的 ?wsa=
此代码块在浮窗中显示
本地联调小贴士
wsa有效期只有 5 分钟,是一次性引导令牌——换成自有会话后,后续请求不要再带wsa。audience必须精确等于你注册的 host(app.example.com),端口/协议不参与aud,但 host 必须一致,否则WrongAudience。- 校验失败先看
WsaVerificationError.code(InvalidSignature/Expired/WrongAudience…),对照 07-常见问题与排错。 - 想不建会话、只读取身份用于展示?把
consumeHandoff换成readHandoffToken()即可(见 02 消费三档)。
下一步:读 02-核心概念 理解令牌契约与两种模式,或直接进 03-推式接入 / 04-拉式登录 看完整细节。
把你自己的 Web 系统作为「扩展应用」接入 GPTBots 工作空间。当工作空间用户从 工作空间 → 扩展 (Extensions) 打开你的应用时,GPTBots 会把用户的工作空间身份以一枚短期签名令牌(wsa,一个 JWT)安全地交接给你,你的应用据此免登录识别用户、按角色开放功能。
目录导航
| 文件 | 读者 | 内容 |
|---|---|---|
| 快速开始.md | 所有人 | 10 分钟跑通一次完整身份握手(含最小可用的前端 + 后端代码) |
| 核心概念.md | 所有人 | 两层扩展应用、两种集成模式、auth_mode、消费三档、wsa 令牌契约 |
| 推式接入-身份握手.md | 推式接入方 | 用户在扩展页点开应用 → 平台把 wsa 推给你(?wsa= 落地页) |
| 拉式登录-Login-with-GPTBots.md | 拉式接入方 | 你的应用放一个「Login with GPTBots Workspace」按钮(OAuth2 授权码 + PKCE) |
| 令牌校验与安全.md | 所有人(必读) | 强制校验清单、密钥保管、不用 SDK 时的多语言校验(Java / Node / Python) |
| SDK-API-参考.md | 所有人 | 两个 SDK 包的完整 API、类型、错误码 |
| 常见问题与排错.md | 所有人 | FAQ、错误码总表、常见坑 |
SDK 一览
官方 SDK 是框架无关、零运行时依赖的两个包(都在压缩包 workspace-extension-sdk-0.1.0.zip 里):
| 包名 | 运行位置 | 作用 |
|---|---|---|
@gptbots/workspace-extension-verify |
你的后端(Node) | 用密钥校验 wsa → 得到 WorkspaceIdentity;后端换取 code → wsa |
@gptbots/workspace-extension-sdk |
浏览器 | 读取 / 抹除 / 交换 wsa;发起「Login with GPTBots Workspace」 |
不想用 SDK 也可以——
wsa是标准 HS256 JWT,任意语言的 JWT 库都能校验。见 05-令牌校验与安全。
从压缩包本地安装
# 解压后,两个包都自带预构建 dist,可直接本地依赖:
unzip workspace-extension-sdk-0.1.0.zip
npm i ./workspace-extension-sdk/packages/verify # 后端
npm i ./workspace-extension-sdk/packages/browser # 前端
# 解压后,两个包都自带预构建 dist,可直接本地依赖:
unzip workspace-extension-sdk-0.1.0.zip
npm i ./workspace-extension-sdk/packages/verify # 后端
npm i ./workspace-extension-sdk/packages/browser # 前端
此代码块在浮窗中显示
SDK 公开发布到 npm 后,可直接
npm i @gptbots/workspace-extension-verify/npm i @gptbots/workspace-extension-sdk。
接入前对齐清单
- 已确定你已创建组织扩展应用并拿到对应密钥
- 已确定集成模式:推式 或 拉式(M-Auth)
-
app_home_url(推式)/redirect_uri的 host(拉式)与注册信息完全一致 - 后端已实现
wsa校验:签名 +iss+aud+exp四项强制校验 - 落地后立刻
history.replaceState抹除 URL 中的wsa/code - 已把令牌换成应用自有会话,后续请求不再透传
wsa - 应用服务器时钟已 NTP 同步
