logo
Développement
Rechercher
Référence de l'API SDK

Référence de l'API SDK

Le SDK officiel se compose de deux paquets indépendants du framework et sans dépendance d'exécution, dont le code source est disponible sur GitHub à l'adresse : https://github.com/gptbots/workspace-extension-sdk.

Nom du paquet Emplacement d'exécution Point d'entrée
@gptbots/workspace-extension-verify Backend (Node ≥ 18) verifyWsa, exchangeWorkspaceCode
@gptbots/workspace-extension-sdk Navigateur consumeHandoff, startWorkspaceLogin, completeWorkspaceLogin

Les deux paquets sont en ESM ("type": "module"). Pour un projet CommonJS, utilisez un import() dynamique ou passez à ESM.


Paquet backend @gptbots/workspace-extension-verify

verifyWsa(token, options): WorkspaceIdentity

Vérifie le wsa et retourne l'identité de l'espace de travail. Ordre de vérification : signature → issaud → expiration (exp±leeway, avec iat/nbf) → claims obligatoires.

interface VerifyOptions { secret?: string; // clé HS256 (per-app ou partagée). Obligatoire pour vérifier en HS256 publicKey?: string; // clé publique PEM RS256. Obligatoire pour vérifier en RS256 (feuille de route) audience: string; // host de l'application d'extension, doit être égal au aud du jeton (obligatoire) issuer?: string; // par défaut 'gptbots-workspace' leewaySeconds?: number; // tolérance de dérive d'horloge (secondes), 30 par défaut algorithms?: ('HS256' | 'RS256')[]; // par défaut ['HS256'] } interface WorkspaceIdentity { accountId: string; // = JWT sub role: 'OWNER' | 'ADMIN' | 'MEMBER'; // valeur inconnue normalisée en MEMBER workspaceId: string; // = JWT workspace_id username?: string; email?: string; avatar?: string; appName?: string; issuedAt?: number; // = iat (secondes) expiresAt?: number; // = exp (secondes) }
                      
                      interface VerifyOptions {
  secret?: string;        // clé HS256 (per-app ou partagée). Obligatoire pour vérifier en HS256
  publicKey?: string;     // clé publique PEM RS256. Obligatoire pour vérifier en RS256 (feuille de route)
  audience: string;       // host de l'application d'extension, doit être égal au aud du jeton (obligatoire)
  issuer?: string;        // par défaut 'gptbots-workspace'
  leewaySeconds?: number; // tolérance de dérive d'horloge (secondes), 30 par défaut
  algorithms?: ('HS256' | 'RS256')[]; // par défaut ['HS256']
}

interface WorkspaceIdentity {
  accountId: string;                  // = JWT sub
  role: 'OWNER' | 'ADMIN' | 'MEMBER'; // valeur inconnue normalisée en MEMBER
  workspaceId: string;                // = JWT workspace_id
  username?: string; email?: string; avatar?: string; appName?: string;
  issuedAt?: number;   // = iat (secondes)
  expiresAt?: number;  // = exp (secondes)
}

                    
Ce bloc de code dans la fenêtre flottante
  • exp obligatoire : absent ou nombre non fini → lève MissingClaim (ne sera pas traité comme « n'expire jamais »).
  • iat / nbf (si présents) dans le futur au-delà de la leeway → lève NotYetValid.
  • En cas d'échec, lève WsaVerificationError (avec .code) ; une erreur de configuration de l'appelant (ex. clé manquante) lève TypeError.

Valeurs de WsaVerificationError.code :

code Signification
InvalidToken Jeton vide / structure invalide / segment n'est pas un objet JSON
InvalidSignature Signature non concordante (clé erronée, jeton altéré)
Expired Expiré (avant exp + leeway)
NotYetValid iat/nbf dans le futur (au-delà de la leeway)
WrongIssuer iss ≠ valeur attendue
WrongAudience aud ≠ votre audience
MissingClaim Manque exp / sub / role / workspace_id
UnsupportedAlgorithm alg absent de la liste blanche (par défaut uniquement HS256)

exchangeWorkspaceCode(options): Promise<WorkspaceCodeExchangeResult>

Pour la connexion pull : dans le backend de l'application d'extension, échange le code à usage unique + codeVerifier PKCE contre un wsa. Ne l'appelez jamais dans le navigateur.

interface ExchangeWorkspaceCodeOptions { tokenUrl: string; // endpoint /token de la plateforme (URL absolue) code: string; // code d'autorisation à usage unique obtenu au callback codeVerifier: string; // verifier PKCE correspondant au code_challenge fetch?: typeof fetch; // fetch injectable (tests/anciens runtimes) ; fetch global par défaut timeoutMs?: number; // timeout de requête, 10000 par défaut ; passez 0 pour désactiver } interface WorkspaceCodeExchangeResult { wsa: string; // le wsa signé, à passer à verifyWsa tokenType?: string; // 'Bearer' expiresIn?: number; // durée de validité du wsa (secondes) }
                      
                      interface ExchangeWorkspaceCodeOptions {
  tokenUrl: string;      // endpoint /token de la plateforme (URL absolue)
  code: string;          // code d'autorisation à usage unique obtenu au callback
  codeVerifier: string;  // verifier PKCE correspondant au code_challenge
  fetch?: typeof fetch;  // fetch injectable (tests/anciens runtimes) ; fetch global par défaut
  timeoutMs?: number;    // timeout de requête, 10000 par défaut ; passez 0 pour désactiver
}

interface WorkspaceCodeExchangeResult {
  wsa: string;           // le wsa signé, à passer à verifyWsa
  tokenType?: string;    // 'Bearer'
  expiresIn?: number;    // durée de validité du wsa (secondes)
}

                    
Ce bloc de code dans la fenêtre flottante
  • Échec de transport / non 2xx / code≠0 métier (ex. 403209 invalid_grant, 403210 invalid_verifier) / data.wsa manquant → lève Error.
  • En cas de timeout, lève token exchange timed out after <ms>ms (10s par défaut, empêche qu'une réponse lente de la plateforme bloque votre backend).

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

                    
Ce bloc de code dans la fenêtre flottante

Paquet navigateur @gptbots/workspace-extension-sdk

Push (Push handoff)

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

Fonction pure, lit le wsa brut depuis la query string. search vaut par défaut location.search, paramName vaut par défaut 'wsa'.

stripHandoffToken(paramName?, ctx?): void

Supprime le wsa de l'URL courante via history.replaceState, pour qu'il ne subsiste ni dans la barre d'adresse, ni dans l'historique, ni dans le Referer. No-op sûr hors navigateur. ctx?: { history?, location? } est injectable (tests).

consumeHandoff(options): Promise<WorkspaceIdentity>

Flux pratique du niveau use : lit le wsa → POST vers le backend de l'application d'extension → après succès, efface le wsa → retourne l'identité.

interface ConsumeHandoffOptions { exchangeUrl: string; // l'interface de vérification backend de l'application d'extension fetch?: typeof fetch; // injectable search?: string; // par défaut location.search paramName?: string; // par défaut 'wsa' strip?: boolean; // par défaut true (efface uniquement en cas de succès) }
                      
                      interface ConsumeHandoffOptions {
  exchangeUrl: string;   // l'interface de vérification backend de l'application d'extension
  fetch?: typeof fetch;  // injectable
  search?: string;       // par défaut location.search
  paramName?: string;    // par défaut 'wsa'
  strip?: boolean;       // par défaut true (efface uniquement en cas de succès)
}

                    
Ce bloc de code dans la fenêtre flottante

Le jeton n'est effacé qu'après un échange réussi, afin qu'un échec transitoire puisse être réessayé par actualisation. Si vous souhaitez l'effacer immédiatement même en cas d'échec, appelez manuellement stripHandoffToken() dans le catch.

Pull (Pull / M-Auth)

startWorkspaceLogin(options): Promise<WorkspaceLoginRequest>

Génère le PKCE, stocke dans sessionStorage, construit et (par défaut) redirige vers /authorize. Nécessite un contexte sécurisé (HTTPS/localhost).

interface StartWorkspaceLoginOptions { authorizeUrl: string; // endpoint /authorize de la plateforme (URL absolue) clientId: string; // l'app home URL de l'application d'extension redirectUri: string; // adresse de callback, le host doit être du même domaine que clientId state?: string; // par défaut, génère automatiquement un state CSRF aléatoire de 16 octets workspaceId?: string; // espace de travail présélectionné, saute la page de sélection d'organisation storage?: StorageLike; // par défaut sessionStorage redirect?: (url: string) => void; // par défaut location.assign navigate?: boolean; // false = construit seulement l'URL sans rediriger (popup/tests) } interface WorkspaceLoginRequest { url: string; state: string; codeVerifier: string; }
                      
                      interface StartWorkspaceLoginOptions {
  authorizeUrl: string;  // endpoint /authorize de la plateforme (URL absolue)
  clientId: string;      // l'app home URL de l'application d'extension
  redirectUri: string;   // adresse de callback, le host doit être du même domaine que clientId
  state?: string;        // par défaut, génère automatiquement un state CSRF aléatoire de 16 octets
  workspaceId?: string;  // espace de travail présélectionné, saute la page de sélection d'organisation
  storage?: StorageLike; // par défaut sessionStorage
  redirect?: (url: string) => void; // par défaut location.assign
  navigate?: boolean;    // false = construit seulement l'URL sans rediriger (popup/tests)
}
interface WorkspaceLoginRequest { url: string; state: string; codeVerifier: string; }

                    
Ce bloc de code dans la fenêtre flottante

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

Sur la page d'atterrissage du callback : lit le code + state, vérifie le state (CSRF), retourne le code + le codeVerifier stocké. Ne consomme pas la requête stockée (la consommation est différée jusqu'à un échange réussi), afin qu'un échec transitoire puisse être réessayé par actualisation. Retourne null en l'absence de code et d'error.

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

                    
Ce bloc de code dans la fenêtre flottante

stripAuthorizeCallback(ctx?): void

Supprime le code / state de l'URL courante.

completeWorkspaceLogin(options): Promise<WorkspaceIdentity>

Flux pratique du niveau use : lit et vérifie le callback → POST {code, codeVerifier} vers votre backend → après succès, efface l'URL → retourne l'identité.

interface CompleteWorkspaceLoginOptions { exchangeUrl: string; // votre propre interface d'échange backend fetch?: typeof fetch; search?: string; // par défaut location.search storage?: StorageLike; // par défaut sessionStorage strip?: boolean; // par défaut true }
                      
                      interface CompleteWorkspaceLoginOptions {
  exchangeUrl: string;   // votre propre interface d'échange backend
  fetch?: typeof fetch;
  search?: string;       // par défaut location.search
  storage?: StorageLike; // par défaut sessionStorage
  strip?: boolean;       // par défaut true
}

                    
Ce bloc de code dans la fenêtre flottante

Valeurs de WorkspaceLoginError.code

code Signification
NoCallback Pas de code d'autorisation dans l'URL
MissingRequest Requête de connexion stockée absente/corrompue (appelez d'abord startWorkspaceLogin)
StateMismatch Échec de la vérification CSRF du state
AuthorizeError Le callback est une redirection d'erreur OAuth (?error=...)
NoFetch Aucune implémentation de fetch disponible
ExchangeFailed L'interface d'échange a retourné un code non 2xx
InvalidResponse L'interface d'échange a retourné un JSON invalide
CryptoUnavailable Pas de Web Crypto (nécessite un contexte sécurisé HTTPS/localhost)
StorageUnavailable Pas de sessionStorage (impossible de stocker le verifier PKCE)
InvalidAuthorizeUrl authorizeUrl n'est pas une URL absolue

Trois. Installation locale depuis l'archive

unzip workspace-extension-sdk-0.1.0.zip # Les deux paquets ont un dist pré-compilé (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
# Les deux paquets ont un dist pré-compilé (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

                    
Ce bloc de code dans la fenêtre flottante

Pour compiler / exécuter les tests vous-même :

cd workspace-extension-sdk npm install npm run build # tsc → dist de chaque paquet npm test # node --test (aucune dépendance de test externe, nécessite Node ≥ 22.6 pour exécuter directement les .ts) npm run type-check # tsc --noEmit
                      
                      cd workspace-extension-sdk
npm install
npm run build       # tsc → dist de chaque paquet
npm test            # node --test (aucune dépendance de test externe, nécessite Node ≥ 22.6 pour exécuter directement les .ts)
npm run type-check  # tsc --noEmit

                    
Ce bloc de code dans la fenêtre flottante