SDK API 參考
官方 SDK 是兩個框架無關、零執行時相依的套件,可以在 GitHub 中取得原始碼,位址為:https://github.com/gptbots/workspace-extension-sdk。
| 套件名 | 執行位置 | 入口 |
|---|---|---|
@gptbots/workspace-extension-verify |
後端(Node ≥ 18) | verifyWsa, exchangeWorkspaceCode |
@gptbots/workspace-extension-sdk |
瀏覽器 | consumeHandoff, startWorkspaceLogin, completeWorkspaceLogin … |
兩個套件均為 ESM(
"type": "module")。CommonJS 專案請用動態import()或改用 ESM。
後端套件 @gptbots/workspace-extension-verify
verifyWsa(token, options): WorkspaceIdentity
校驗 wsa 並回傳工作空間身份。校驗順序:簽章 → iss → aud → 過期(exp±leeway,含 iat/nbf) → 必填聲明。
interface VerifyOptions {
secret?: string; // HS256 金鑰(per-app 或共享)。校驗 HS256 時必填
publicKey?: string; // RS256 PEM 公鑰。校驗 RS256 時必填(路線圖)
audience: string; // 擴充應用程式 host,必須等於令牌 aud(必填)
issuer?: string; // 預設 'gptbots-workspace'
leewaySeconds?: number; // 時鐘漂移容忍(秒),預設 30
algorithms?: ('HS256' | 'RS256')[]; // 預設 ['HS256']
}
interface WorkspaceIdentity {
accountId: string; // = JWT sub
role: 'OWNER' | 'ADMIN' | 'MEMBER'; // 未知值正規化為 MEMBER
workspaceId: string; // = JWT workspace_id
username?: string; email?: string; avatar?: string; appName?: string;
issuedAt?: number; // = iat(秒)
expiresAt?: number; // = exp(秒)
}
exp強制:缺失或非有限數字 → 拋MissingClaim(不會被當作「永不過期」)。iat/nbf(如存在)在未來超出 leeway → 拋NotYetValid。- 失敗拋
WsaVerificationError(帶.code);呼叫方設定錯誤(如缺金鑰)拋TypeError。
WsaVerificationError.code 取值:
| code | 含義 |
|---|---|
InvalidToken |
令牌為空 / 結構非法 / segment 不是 JSON 物件 |
InvalidSignature |
簽章不匹配(金鑰錯、被竄改) |
Expired |
已過期(exp + leeway 之前) |
NotYetValid |
iat/nbf 在未來(超出 leeway) |
WrongIssuer |
iss ≠ 期望值 |
WrongAudience |
aud ≠ 你的 audience |
MissingClaim |
缺 exp / sub / role / workspace_id |
UnsupportedAlgorithm |
alg 不在白名單(預設僅 HS256) |
exchangeWorkspaceCode(options): Promise<WorkspaceCodeExchangeResult>
拉式登入用:在擴充應用程式後端把一次性 code + PKCE codeVerifier 換成 wsa。切勿在瀏覽器呼叫。
interface ExchangeWorkspaceCodeOptions {
tokenUrl: string; // 平台 /token 端點(絕對 URL)
code: string; // 回呼拿到的一次性授權碼
codeVerifier: string; // 與 code_challenge 對應的 PKCE verifier
fetch?: typeof fetch; // 可注入 fetch(測試/舊執行時);預設全域 fetch
timeoutMs?: number; // 請求逾時,預設 10000;傳 0 關閉
}
interface WorkspaceCodeExchangeResult {
wsa: string; // 簽章後的 wsa,交給 verifyWsa
tokenType?: string; // 'Bearer'
expiresIn?: number; // wsa 有效期(秒)
}
- 傳輸失敗 / 非 2xx / 業務
code≠0(如403209 invalid_grant、403210 invalid_verifier)/ 缺data.wsa→ 拋Error。 - 逾時拋
token exchange timed out after <ms>ms(預設 10s,防平台慢回應掛住你的後端)。
Express 中介軟體範例
import { verifyWsa, WsaVerificationError } from '@gptbots/workspace-extension-verify';
export function requireWorkspaceIdentity(secret: string, audience: string) {
return (req, res, next) => {
try {
req.identity = verifyWsa(req.body.wsa, { secret, audience });
next();
} catch (e) {
const code = e instanceof WsaVerificationError ? e.code : 'Error';
res.status(401).json({ code });
}
};
}
瀏覽器套件 @gptbots/workspace-extension-sdk
推式(Push handoff)
readHandoffToken(search?, paramName?): string | null
純函數,從查詢串讀取原始 wsa。search 預設 location.search,paramName 預設 'wsa'。
stripHandoffToken(paramName?, ctx?): void
用 history.replaceState 從目前 URL 移除 wsa,讓它不殘留在網址列/歷史/Referer。瀏覽器外安全空操作。ctx?: { history?, location? } 可注入(測試)。
consumeHandoff(options): Promise<WorkspaceIdentity>
use 檔便捷流:讀 wsa → POST 至擴充應用程式後端 → 成功後抹除 wsa → 回傳身份。
interface ConsumeHandoffOptions {
exchangeUrl: string; // 擴充應用程式的後端校驗介面
fetch?: typeof fetch; // 可注入
search?: string; // 預設 location.search
paramName?: string; // 預設 'wsa'
strip?: boolean; // 預設 true(僅成功時抹除)
}
令牌僅成功交換後抹除,以便瞬時失敗可重新整理重試。若希望失敗也立即抹除,
catch後手動stripHandoffToken()。
拉式(Pull / M-Auth)
startWorkspaceLogin(options): Promise<WorkspaceLoginRequest>
產生 PKCE、存 sessionStorage、建構並(預設)跳轉到 /authorize。需要安全上下文(HTTPS/localhost)。
interface StartWorkspaceLoginOptions {
authorizeUrl: string; // 平台 /authorize 端點(絕對 URL)
clientId: string; // 擴充應用程式的 app home URL
redirectUri: string; // 回呼位址,host 須與 clientId 同域
state?: string; // 預設自動產生 16 位元組隨機 CSRF state
workspaceId?: string; // 預選工作空間,跳過組織選擇頁
storage?: StorageLike; // 預設 sessionStorage
redirect?: (url: string) => void; // 預設 location.assign
navigate?: boolean; // false = 只建構 URL 不跳轉(popup/測試)
}
interface WorkspaceLoginRequest { url: string; state: string; codeVerifier: string; }
readAuthorizeCallback(search?, storage?): AuthorizeCallback | null
在回呼落地頁:讀 code + state,校驗 state(CSRF),回傳 code + 儲存的 codeVerifier。不會消費儲存的請求(消費延遲到成功交換),以便瞬時失敗可重新整理重試。無 code 且無 error 時回傳 null。
interface AuthorizeCallback { code: string; state: string | null; codeVerifier: string; }
stripAuthorizeCallback(ctx?): void
從目前 URL 移除 code / state。
completeWorkspaceLogin(options): Promise<WorkspaceIdentity>
use 檔便捷流:讀並校驗回呼 → POST {code, codeVerifier} 給你後端 → 成功後抹除 URL → 回傳身份。
interface CompleteWorkspaceLoginOptions {
exchangeUrl: string; // 你自己的後端換取介面
fetch?: typeof fetch;
search?: string; // 預設 location.search
storage?: StorageLike; // 預設 sessionStorage
strip?: boolean; // 預設 true
}
WorkspaceLoginError.code 取值
| code | 含義 |
|---|---|
NoCallback |
URL 裡沒有授權碼 |
MissingRequest |
沒有/損壞的已存登入請求(先呼叫 startWorkspaceLogin) |
StateMismatch |
state CSRF 校驗不通過 |
AuthorizeError |
回呼是 OAuth 錯誤重新導向(?error=...) |
NoFetch |
無可用 fetch 實作 |
ExchangeFailed |
交換介面回傳非 2xx |
InvalidResponse |
交換介面回傳非法 JSON |
CryptoUnavailable |
無 Web Crypto(需 HTTPS/localhost 安全上下文) |
StorageUnavailable |
無 sessionStorage(無法存 PKCE verifier) |
InvalidAuthorizeUrl |
authorizeUrl 不是絕對 URL |
三、從壓縮檔本地安裝
unzip workspace-extension-sdk-0.1.0.zip
# 兩個套件已預建置 dist(main=dist/index.js, types=dist/index.d.ts)
npm i ./workspace-extension-sdk/packages/verify # 後端
npm i ./workspace-extension-sdk/packages/browser # 前端
如需自行建置 / 跑測試:
cd workspace-extension-sdk
npm install
npm run build # tsc → 各套件 dist
npm test # node --test(零外部測試相依,需 Node ≥ 22.6 以直跑 .ts)
npm run type-check # tsc --noEmit
