logo
Entwicklung
Suchen
SDK-API-Referenz

SDK-API-Referenz

Die offiziellen SDKs sind zwei framework-unabhängige Pakete ohne Laufzeitabhängigkeiten; den Quellcode erhalten Sie auf GitHub unter: https://github.com/gptbots/workspace-extension-sdk.

Paketname Laufort Einstiegspunkte
@gptbots/workspace-extension-verify Backend (Node ≥ 18) verifyWsa, exchangeWorkspaceCode
@gptbots/workspace-extension-sdk Browser consumeHandoff, startWorkspaceLogin, completeWorkspaceLogin

Beide Pakete sind ESM ("type": "module"). Verwenden Sie in CommonJS-Projekten ein dynamisches import() oder wechseln Sie zu ESM.


Backend-Paket @gptbots/workspace-extension-verify

verifyWsa(token, options): WorkspaceIdentity

Prüft das wsa und gibt die Workspace-Identität zurück. Prüfreihenfolge: Signatur → issaud → Ablauf (exp±leeway, inkl. iat/nbf) → Pflichtangaben.

interface VerifyOptions { secret?: string; // HS256-Schlüssel (per-App oder geteilt). Bei HS256-Prüfung erforderlich publicKey?: string; // RS256-PEM-Public-Key. Bei RS256-Prüfung erforderlich (Roadmap) audience: string; // Erweiterungs-App-Host, muss gleich dem Token-aud sein (erforderlich) issuer?: string; // Standard 'gptbots-workspace' leewaySeconds?: number; // Toleranz für Uhrenabweichung (Sekunden), Standard 30 algorithms?: ('HS256' | 'RS256')[]; // Standard ['HS256'] } interface WorkspaceIdentity { accountId: string; // = JWT sub role: 'OWNER' | 'ADMIN' | 'MEMBER'; // unbekannte Werte werden zu MEMBER normalisiert workspaceId: string; // = JWT workspace_id username?: string; email?: string; avatar?: string; appName?: string; issuedAt?: number; // = iat (Sekunden) expiresAt?: number; // = exp (Sekunden) }
                      
                      interface VerifyOptions {
  secret?: string;        // HS256-Schlüssel (per-App oder geteilt). Bei HS256-Prüfung erforderlich
  publicKey?: string;     // RS256-PEM-Public-Key. Bei RS256-Prüfung erforderlich (Roadmap)
  audience: string;       // Erweiterungs-App-Host, muss gleich dem Token-aud sein (erforderlich)
  issuer?: string;        // Standard 'gptbots-workspace'
  leewaySeconds?: number; // Toleranz für Uhrenabweichung (Sekunden), Standard 30
  algorithms?: ('HS256' | 'RS256')[]; // Standard ['HS256']
}

interface WorkspaceIdentity {
  accountId: string;                  // = JWT sub
  role: 'OWNER' | 'ADMIN' | 'MEMBER'; // unbekannte Werte werden zu MEMBER normalisiert
  workspaceId: string;                // = JWT workspace_id
  username?: string; email?: string; avatar?: string; appName?: string;
  issuedAt?: number;   // = iat (Sekunden)
  expiresAt?: number;  // = exp (Sekunden)
}

                    
Dieser Codeblock im schwebenden Fenster
  • exp verpflichtend: fehlend oder keine endliche Zahl → wirft MissingClaim (wird nicht als „läuft nie ab" behandelt).
  • iat / nbf (falls vorhanden) in der Zukunft über die Leeway hinaus → wirft NotYetValid.
  • Bei Fehlschlag wird WsaVerificationError geworfen (mit .code); bei einer Fehlkonfiguration der aufrufenden Seite (z. B. fehlender Schlüssel) wird TypeError geworfen.

Werte von WsaVerificationError.code:

code Bedeutung
InvalidToken Token leer / strukturell ungültig / segment ist kein JSON-Objekt
InvalidSignature Signatur stimmt nicht (falscher Schlüssel, manipuliert)
Expired Abgelaufen (vor exp + leeway)
NotYetValid iat/nbf in der Zukunft (über die Leeway hinaus)
WrongIssuer iss ≠ erwarteter Wert
WrongAudience aud ≠ Ihre audience
MissingClaim Fehlendes exp / sub / role / workspace_id
UnsupportedAlgorithm alg nicht in der Whitelist (standardmäßig nur HS256)

exchangeWorkspaceCode(options): Promise<WorkspaceCodeExchangeResult>

Für den Pull-Login: im Backend der Erweiterungs-App den einmaligen code + PKCE-codeVerifier gegen ein wsa eintauschen. Auf keinen Fall im Browser aufrufen.

interface ExchangeWorkspaceCodeOptions { tokenUrl: string; // /token-Endpunkt der Plattform (absolute URL) code: string; // der beim Callback erhaltene einmalige Autorisierungscode codeVerifier: string; // der zum code_challenge passende PKCE-verifier fetch?: typeof fetch; // injizierbares fetch (Tests/alte Laufzeiten); Standard globales fetch timeoutMs?: number; // Anfrage-Timeout, Standard 10000; 0 zum Deaktivieren übergeben } interface WorkspaceCodeExchangeResult { wsa: string; // das signierte wsa, an verifyWsa übergeben tokenType?: string; // 'Bearer' expiresIn?: number; // Gültigkeit des wsa (Sekunden) }
                      
                      interface ExchangeWorkspaceCodeOptions {
  tokenUrl: string;      // /token-Endpunkt der Plattform (absolute URL)
  code: string;          // der beim Callback erhaltene einmalige Autorisierungscode
  codeVerifier: string;  // der zum code_challenge passende PKCE-verifier
  fetch?: typeof fetch;  // injizierbares fetch (Tests/alte Laufzeiten); Standard globales fetch
  timeoutMs?: number;    // Anfrage-Timeout, Standard 10000; 0 zum Deaktivieren übergeben
}

interface WorkspaceCodeExchangeResult {
  wsa: string;           // das signierte wsa, an verifyWsa übergeben
  tokenType?: string;    // 'Bearer'
  expiresIn?: number;    // Gültigkeit des wsa (Sekunden)
}

                    
Dieser Codeblock im schwebenden Fenster
  • Übertragungsfehler / non-2xx / Business-code≠0 (z. B. 403209 invalid_grant, 403210 invalid_verifier) / fehlendes data.wsa → wirft Error.
  • Timeout wirft token exchange timed out after <ms>ms (Standard 10s, verhindert, dass eine langsame Plattformantwort Ihr Backend blockiert).

Beispiel für eine Express-Middleware

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 }); } }; }
                      
                      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 });
    }
  };
}

                    
Dieser Codeblock im schwebenden Fenster

Browser-Paket @gptbots/workspace-extension-sdk

Push (Push handoff)

readHandoffToken(search?, paramName?): string | null

Reine Funktion, liest das rohe wsa aus dem Query-String. search standardmäßig location.search, paramName standardmäßig 'wsa'.

stripHandoffToken(paramName?, ctx?): void

Entfernt mit history.replaceState das wsa aus der aktuellen URL, damit es nicht in Adressleiste/Verlauf/Referer zurückbleibt. Außerhalb des Browsers eine sichere No-Op. ctx?: { history?, location? } kann injiziert werden (Tests).

consumeHandoff(options): Promise<WorkspaceIdentity>

Bequemer Ablauf der use-Stufe: wsa lesen → POST an das Backend der Erweiterungs-App → nach Erfolg das wsa entfernen → Identität zurückgeben.

interface ConsumeHandoffOptions { exchangeUrl: string; // Backend-Prüfschnittstelle der Erweiterungs-App fetch?: typeof fetch; // injizierbar search?: string; // Standard location.search paramName?: string; // Standard 'wsa' strip?: boolean; // Standard true (nur bei Erfolg entfernen) }
                      
                      interface ConsumeHandoffOptions {
  exchangeUrl: string;   // Backend-Prüfschnittstelle der Erweiterungs-App
  fetch?: typeof fetch;  // injizierbar
  search?: string;       // Standard location.search
  paramName?: string;    // Standard 'wsa'
  strip?: boolean;       // Standard true (nur bei Erfolg entfernen)
}

                    
Dieser Codeblock im schwebenden Fenster

Das Token wird nur nach erfolgreichem Austausch entfernt, damit ein vorübergehender Fehler durch Neuladen erneut versucht werden kann. Soll es auch bei Fehlschlag sofort entfernt werden, im catch manuell stripHandoffToken() aufrufen.

Pull (Pull / M-Auth)

startWorkspaceLogin(options): Promise<WorkspaceLoginRequest>

Generiert PKCE, speichert im sessionStorage, baut die URL auf und (standardmäßig) leitet zu /authorize weiter. Erfordert einen sicheren Kontext (HTTPS/localhost).

interface StartWorkspaceLoginOptions { authorizeUrl: string; // /authorize-Endpunkt der Plattform (absolute URL) clientId: string; // app home URL der Erweiterungs-App redirectUri: string; // Callback-Adresse, host muss dieselbe Domain wie clientId haben state?: string; // standardmäßig automatisch generierter 16-Byte-Zufalls-CSRF-state workspaceId?: string; // vorausgewählter Workspace, überspringt die Organisationsauswahlseite storage?: StorageLike; // Standard sessionStorage redirect?: (url: string) => void; // Standard location.assign navigate?: boolean; // false = nur URL bauen, nicht weiterleiten (popup/Tests) } interface WorkspaceLoginRequest { url: string; state: string; codeVerifier: string; }
                      
                      interface StartWorkspaceLoginOptions {
  authorizeUrl: string;  // /authorize-Endpunkt der Plattform (absolute URL)
  clientId: string;      // app home URL der Erweiterungs-App
  redirectUri: string;   // Callback-Adresse, host muss dieselbe Domain wie clientId haben
  state?: string;        // standardmäßig automatisch generierter 16-Byte-Zufalls-CSRF-state
  workspaceId?: string;  // vorausgewählter Workspace, überspringt die Organisationsauswahlseite
  storage?: StorageLike; // Standard sessionStorage
  redirect?: (url: string) => void; // Standard location.assign
  navigate?: boolean;    // false = nur URL bauen, nicht weiterleiten (popup/Tests)
}
interface WorkspaceLoginRequest { url: string; state: string; codeVerifier: string; }

                    
Dieser Codeblock im schwebenden Fenster

readAuthorizeCallback(search?, storage?): AuthorizeCallback | null

Auf der Callback-Landingpage: liest code + state, prüft state (CSRF), gibt code + den gespeicherten codeVerifier zurück. Konsumiert die gespeicherte Anfrage nicht (das Konsumieren wird bis zum erfolgreichen Austausch verzögert), damit ein vorübergehender Fehler durch Neuladen erneut versucht werden kann. Gibt null zurück, wenn kein code und kein error vorhanden ist.

interface AuthorizeCallback { code: string; state: string | null; codeVerifier: string; }
                      
                      interface AuthorizeCallback { code: string; state: string | null; codeVerifier: string; }

                    
Dieser Codeblock im schwebenden Fenster

stripAuthorizeCallback(ctx?): void

Entfernt code / state aus der aktuellen URL.

completeWorkspaceLogin(options): Promise<WorkspaceIdentity>

Bequemer Ablauf der use-Stufe: Callback lesen und prüfen → POST {code, codeVerifier} an Ihr Backend → nach Erfolg die URL bereinigen → Identität zurückgeben.

interface CompleteWorkspaceLoginOptions { exchangeUrl: string; // Ihre eigene Backend-Austauschschnittstelle fetch?: typeof fetch; search?: string; // Standard location.search storage?: StorageLike; // Standard sessionStorage strip?: boolean; // Standard true }
                      
                      interface CompleteWorkspaceLoginOptions {
  exchangeUrl: string;   // Ihre eigene Backend-Austauschschnittstelle
  fetch?: typeof fetch;
  search?: string;       // Standard location.search
  storage?: StorageLike; // Standard sessionStorage
  strip?: boolean;       // Standard true
}

                    
Dieser Codeblock im schwebenden Fenster

Werte von WorkspaceLoginError.code

code Bedeutung
NoCallback Kein Autorisierungscode in der URL
MissingRequest Keine/beschädigte gespeicherte Anmeldeanfrage (zuerst startWorkspaceLogin aufrufen)
StateMismatch state-CSRF-Prüfung fehlgeschlagen
AuthorizeError Callback ist eine OAuth-Fehlerweiterleitung (?error=...)
NoFetch Keine verfügbare fetch-Implementierung
ExchangeFailed Austauschschnittstelle liefert non-2xx
InvalidResponse Austauschschnittstelle liefert ungültiges JSON
CryptoUnavailable Kein Web Crypto (erfordert sicheren Kontext HTTPS/localhost)
StorageUnavailable Kein sessionStorage (PKCE-verifier kann nicht gespeichert werden)
InvalidAuthorizeUrl authorizeUrl ist keine absolute URL

Drittens: Lokale Installation aus dem Archiv

unzip workspace-extension-sdk-0.1.0.zip # Beide Pakete sind bereits mit dist vorgebaut (main=dist/index.js, types=dist/index.d.ts) npm i ./workspace-extension-sdk/packages/verify # Backend npm i ./workspace-extension-sdk/packages/browser # Frontend
                      
                      unzip workspace-extension-sdk-0.1.0.zip
# Beide Pakete sind bereits mit dist vorgebaut (main=dist/index.js, types=dist/index.d.ts)
npm i ./workspace-extension-sdk/packages/verify   # Backend
npm i ./workspace-extension-sdk/packages/browser  # Frontend

                    
Dieser Codeblock im schwebenden Fenster

Falls Sie selbst bauen / Tests ausführen möchten:

cd workspace-extension-sdk npm install npm run build # tsc → dist je Paket npm test # node --test (keine externen Testabhängigkeiten, benötigt Node ≥ 22.6 zum direkten Ausführen von .ts) npm run type-check # tsc --noEmit
                      
                      cd workspace-extension-sdk
npm install
npm run build       # tsc → dist je Paket
npm test            # node --test (keine externen Testabhängigkeiten, benötigt Node ≥ 22.6 zum direkten Ausführen von .ts)
npm run type-check  # tsc --noEmit

                    
Dieser Codeblock im schwebenden Fenster