Intégration push (poignée de main d'identité / Push Handoff)
Scénario : l'utilisateur clique sur l'application d'extension du développeur depuis la page Espace de travail → Extensions, et la plateforme pousse l'identité de l'utilisateur vers la page d'atterrissage de l'application d'extension cible sous la forme ?wsa=<JWT>. Le développeur la consomme sur la page d'atterrissage, la vérifie côté backend, puis l'échange contre sa propre session.
Dans ce mode, le développeur n'a pas besoin d'appeler l'endpoint de signature de la plateforme — c'est le frontend de l'espace de travail qui l'appelle activement lorsque l'utilisateur clique. Le développeur se charge seulement de « recevoir et vérifier ».
Séquence de bout en bout
sequenceDiagram
autonumber
actor User as L'utilisateur accède à l'application d'extension
participant GB as Plateforme GPTBots
participant FE as Application d'extension-Page d'atterrissage
participant BE as Application d'extension-Backend
User->>GB: Clique sur l'icône de l'application (POST sign-token)
GB->>GB: Vérifie que le cliqueur est membre de ce workspace
GB->>GB: Signe le wsa avec la clé<br/>(JWT 5 minutes, aud=host de l'application d'extension)
GB->>FE: Ouvre app_home_url?wsa=<JWT>
Note over FE: consumeHandoff()<br/>lit le ?wsa=, POST vers le backend de l'application d'extension
FE->>BE: POST /session/exchange { wsa }
BE->>BE: verifyWsa() → identity
BE->>BE: Établit sa propre session
BE-->>FE: Retourne la session (Set-Cookie)
Note over FE: history.replaceState efface le ?wsa=
Règles selon lesquelles la plateforme génère l'URL de redirection (le développeur n'a pas besoin de les implémenter) :
- Fait une correspondance exacte de l'application d'extension cible selon
app_home_urldans les informations enregistrées (toute URL non enregistrée est refusée à la signature, pour éviter la fuite d'identité). - Vérifie que le cliqueur est effectivement membre de cet espace de travail (
workspace_id). - Si
app_home_urlcontient déjà une query, lewsaest ajouté avec&; la valeur duwsaest déjà encodée en URL, vous n'avez pas besoin de la décoder manuellement après l'avoir lue.
Frontend : consommer le wsa de la page d'atterrissage
Avec le SDK (recommandé)
import { consumeHandoff } from '@gptbots/workspace-extension-sdk';
try {
const identity = await consumeHandoff({
exchangeUrl: '/session/exchange', // votre propre interface de vérification backend
// search: location.search, // lit location.search par défaut
// paramName: 'wsa', // nom de paramètre par défaut : wsa
// strip: true, // efface par défaut le wsa de l'URL après succès
});
bootYourApp(identity);
} catch (e) {
// pas de wsa (accès direct de l'utilisateur), ou échec de vérification côté backend
showLoginOrError(e);
}
consumeHandoff fait quatre choses : ① lit le ?wsa= ② POST { wsa } vers exchangeUrl ③ après succès, efface le ?wsa= via history.replaceState ④ retourne l'identity renvoyée par le backend.
Moment de l'effacement : le jeton n'est effacé de l'URL qu'après un échange réussi, afin qu'un échec transitoire puisse être réessayé par actualisation. C'est un jeton à usage unique valable 5 minutes ; si le développeur souhaite l'effacer immédiatement même en cas d'échec, il peut appeler manuellement
stripHandoffToken()dans lecatch.
Sans créer de session, seulement lire l'identité (receive-only)
import { readHandoffToken, stripHandoffToken } from '@gptbots/workspace-extension-sdk';
const token = readHandoffToken(); // fonction pure, retourne la chaîne JWT brute ou null
if (token) {
// il reste recommandé d'envoyer le token à votre backend pour verifyWsa avant de faire confiance à son contenu (ne parsez pas le JWT dans le frontend comme une identité de confiance)
await fetch('/telemetry/visit', { method: 'POST', body: JSON.stringify({ wsa: token }) });
}
stripHandoffToken(); // efface le wsa de l'URL dans tous les cas
⚠️ Ne décodez pas le JWT dans le frontend pour le traiter comme une identité de confiance. La signature du JWT ne peut être vérifiée qu'avec la clé, et la clé n'existe que dans le backend. Le parsing frontend ne peut servir que d'espace réservé « non sécurisé » pour l'affichage ; toute décision d'autorisation doit reposer sur le résultat de
verifyWsacôté backend.
Backend : vérifier le wsa
3.1 Avec le SDK (recommandé)
import { verifyWsa, WsaVerificationError } from '@gptbots/workspace-extension-verify';
app.post('/session/exchange', (req, res) => {
try {
const identity = verifyWsa(req.body.wsa, {
secret: process.env.EXTENSION_APP_SECRET, // clé per-app Tier2 ou clé partagée Tier1
audience: 'app.example.com', // host de l'application d'extension, doit être égal à aud
// issuer: 'gptbots-workspace', // par défaut
// leewaySeconds: 30, // tolérance de dérive d'horloge, 30s par défaut
// algorithms: ['HS256'], // par défaut
});
// Isolation multi-tenant : rattachez la requête à identity.workspaceId
const sid = createSession(identity);
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 });
}
});
Sans le SDK
Le wsa est un JWT HS256 standard, vérifiable par n'importe quelle bibliothèque JWT dans n'importe quel langage. Voir les exemples Java / Node / Python dans 05-Vérification du jeton et sécurité §3. Que vous utilisiez le SDK ou non, les quatre vérifications signature / iss / aud / exp sont obligatoires.
Enregistrer l'application d'extension (obtenir la clé)
Le OWNER/ADMIN de l'espace de travail : Espace de travail → Gestion de l'espace → Applications d'extension → Ajouter, puis remplissez :
| Champ | Description |
|---|---|
| Nom de l'application | Nom d'affichage, ≤ 12 caractères chinois recommandé pour éviter la troncature |
| Icône de l'application | Icône carrée, ≥ 128×128 recommandé |
| URL d'accès de l'application | Votre app_home_url, qui sert de clé unique ; la signature applique une correspondance stricte sur la chaîne complète |
| Mode d'authentification | Choisissez workspace_account (nécessite la transmission de l'identité) |
Après validation, l'App Secret est affiché une seule fois en clair ; copiez-le et sauvegardez-le immédiatement (une fois fermé, il ne peut être que renouvelé).
Liste de vérification de la page d'atterrissage
- Après réception du
?wsa=, envoyez-le d'abord en POST au service backend de l'application d'extension pour vérification, avant de faire confiance à son contenu - Une fois la vérification réussie, effacez immédiatement le
wsaviahistory.replaceState(déjà fait par défaut parconsumeHandoff) - Échangez contre une session propre ; les XHR/fetch/img suivants ne transmettent plus le
wsa - Gérez la branche « accès direct de l'utilisateur, sans
wsa» (redirection vers la connexion ou anonyme) - En cas d'échec de vérification, affichez un message lisible selon le
WsaVerificationError.code
Détails de sécurité et vérification multi-langage : Vérification du jeton et sécurité. Pour passer à un « bouton de connexion dans l'application » : Connexion pull.
