SDK API อ้างอิง
SDK ทางการเป็นสองแพ็กเกจที่ไม่ผูกกับเฟรมเวิร์กและไม่มี dependency ในรันไทม์ สามารถรับซอร์สโค้ดได้ที่ 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) → claim ที่จำเป็น
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 ไม่อยู่ใน whitelist(ค่าเริ่มต้นเฉพาะ HS256) |
exchangeWorkspaceCode(options): Promise<WorkspaceCodeExchangeResult>
ใช้กับการเข้าสู่ระบบแบบ Pull: ที่แบ็กเอนด์ของแอปส่วนขยายให้แลก code ใช้ครั้งเดียว + PKCE codeVerifier เป็น wsa ห้ามเรียกในเบราว์เซอร์เด็ดขาด
interface ExchangeWorkspaceCodeOptions {
tokenUrl: string; // endpoint /token ของแพลตฟอร์ม (absolute URL)
code: string; // authorization code ใช้ครั้งเดียวที่ได้จาก callback
codeVerifier: string; // PKCE verifier ที่ตรงกับ code_challenge
fetch?: typeof fetch; // inject fetch ได้ (ทดสอบ/รันไทม์เก่า); ค่าเริ่มต้น fetch ส่วนกลาง
timeoutMs?: number; // timeout ของคำขอ ค่าเริ่มต้น 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 - timeout โยน
token exchange timed out after <ms>ms(ค่าเริ่มต้น 10s, กันแพลตฟอร์มตอบช้าค้างแบ็กเอนด์ของคุณ)
ตัวอย่าง Middleware ของ 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(Push handoff)
readHandoffToken(search?, paramName?): string | null
ฟังก์ชันบริสุทธิ์ อ่าน wsa ดั้งเดิมจาก query string search ค่าเริ่มต้น location.search, paramName ค่าเริ่มต้น 'wsa'
stripHandoffToken(paramName?, ctx?): void
ใช้ history.replaceState ลบ wsa ออกจาก URL ปัจจุบัน เพื่อไม่ให้ตกค้างในแถบที่อยู่/ประวัติ/Referer นอกเบราว์เซอร์เป็น no-op ที่ปลอดภัย ctx?: { history?, location? } inject ได้(ทดสอบ)
consumeHandoff(options): Promise<WorkspaceIdentity>
ขั้นตอนสะดวกระดับ use: อ่าน wsa → POST ไปยังแบ็กเอนด์ของแอปส่วนขยาย → เมื่อสำเร็จลบ wsa → คืนค่าตัวตน
interface ConsumeHandoffOptions {
exchangeUrl: string; // อินเทอร์เฟซตรวจสอบของแบ็กเอนด์แอปส่วนขยาย
fetch?: typeof fetch; // inject ได้
search?: string; // ค่าเริ่มต้น location.search
paramName?: string; // ค่าเริ่มต้น 'wsa'
strip?: boolean; // ค่าเริ่มต้น true (ลบเฉพาะเมื่อสำเร็จ)
}
โทเค็นจะถูกลบหลังแลกสำเร็จเท่านั้น เพื่อให้ความล้มเหลวชั่วคราวสามารถรีเฟรชลองใหม่ได้ หากต้องการให้ลบทันทีแม้ล้มเหลว ให้
catchแล้วstripHandoffToken()เอง
แบบ Pull(Pull / M-Auth)
startWorkspaceLogin(options): Promise<WorkspaceLoginRequest>
สร้าง PKCE, เก็บใน sessionStorage, สร้างและ(โดยค่าเริ่มต้น)เปลี่ยนเส้นทางไปที่ /authorize ต้องอยู่ในบริบทที่ปลอดภัย(HTTPS/localhost)
interface StartWorkspaceLoginOptions {
authorizeUrl: string; // endpoint /authorize ของแพลตฟอร์ม (absolute URL)
clientId: string; // app home URL ของแอปส่วนขยาย
redirectUri: string; // ที่อยู่ callback, host ต้องอยู่โดเมนเดียวกับ clientId
state?: string; // ค่าเริ่มต้นสร้าง CSRF state สุ่ม 16 ไบต์อัตโนมัติ
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
ที่หน้า Landing ของ callback: อ่าน code + state, ตรวจสอบ state(CSRF), คืนค่า code + codeVerifier ที่เก็บไว้ จะไม่บริโภคคำขอที่เก็บไว้(การบริโภคถูกเลื่อนไปจนแลกสำเร็จ)เพื่อให้ความล้มเหลวชั่วคราวสามารถรีเฟรชลองใหม่ได้ เมื่อไม่มี code และไม่มี error จะคืนค่า null
interface AuthorizeCallback { code: string; state: string | null; codeVerifier: string; }
stripAuthorizeCallback(ctx?): void
ลบ code / state ออกจาก URL ปัจจุบัน
completeWorkspaceLogin(options): Promise<WorkspaceIdentity>
ขั้นตอนสะดวกระดับ use: อ่านและตรวจสอบ callback → 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 |
ไม่มี authorization code ใน URL |
MissingRequest |
ไม่มี/คำขอล็อกอินที่เก็บไว้เสียหาย(เรียก startWorkspaceLogin ก่อน) |
StateMismatch |
การตรวจสอบ CSRF state ไม่ผ่าน |
AuthorizeError |
callback เป็นการเปลี่ยนเส้นทางข้อผิดพลาด OAuth(?error=...) |
NoFetch |
ไม่มี implementation ของ fetch ให้ใช้ |
ExchangeFailed |
อินเทอร์เฟซแลกคืนค่าไม่ใช่ 2xx |
InvalidResponse |
อินเทอร์เฟซแลกคืนค่า JSON ไม่ถูกต้อง |
CryptoUnavailable |
ไม่มี Web Crypto(ต้องบริบทที่ปลอดภัย HTTPS/localhost) |
StorageUnavailable |
ไม่มี sessionStorage(ไม่สามารถเก็บ PKCE verifier) |
InvalidAuthorizeUrl |
authorizeUrl ไม่ใช่ absolute URL |
สาม. ติดตั้งในเครื่องจากไฟล์บีบอัด
unzip workspace-extension-sdk-0.1.0.zip
# ทั้งสองแพ็กเกจ pre-build dist มาแล้ว (main=dist/index.js, types=dist/index.d.ts)
npm i ./workspace-extension-sdk/packages/verify # แบ็กเอนด์
npm i ./workspace-extension-sdk/packages/browser # ฟรอนต์เอนด์
หากต้องการ build เอง / รันทดสอบ:
cd workspace-extension-sdk
npm install
npm run build # tsc → dist ของแต่ละแพ็กเกจ
npm test # node --test (ไม่มี dependency ทดสอบภายนอก, ต้อง Node ≥ 22.6 เพื่อรัน .ts ตรง ๆ)
npm run type-check # tsc --noEmit
