logo
Développement
Rechercher
05 · Vérification du jeton et sécurité

05 · Vérification du jeton et sécurité

Que ce soit en push ou en pull, le développeur finit toujours par obtenir un wsa (JWT HS256). Cet article est une lecture obligatoire pour tous les intégrateurs — une vérification laxiste = une identité falsifiable.

Liste de contrôle de vérification obligatoire

Une fois le wsa obtenu, vérifiez dans le service backend de l'application d'extension, dans l'ordre (verifyWsa intègre déjà tout cela) :

  1. Signature : vérifiez en HS256 avec la clé distribuée/obtenue à l'enregistrement par la plateforme. Échec → rejet.
  2. exp : heure actuelle ≤ exp (avec une légère leeway). L'exp doit exister — un jeton sans exp doit être rejeté directement (sinon il n'expire jamais).
  3. iss : doit être égal à gptbots-workspace.
  4. aud : doit être égal au host de l'application d'extension cible. C'est la ligne de défense clé contre « le vol d'un jeton pour attaquer une autre application », à ne jamais omettre.
  5. iat / nbf (si présents) : ne doivent pas être dans le futur (au-delà de la leeway), pour empêcher qu'un jeton « dont l'heure d'émission est fixée dans un futur lointain » soit utilisable longtemps.
  6. Isolation par workspace_id : si l'extension fait de l'isolation multi-espaces de travail, rattachez la requête à ce workspace_id et interdisez l'accès inter-tenant.
  7. Effacer immédiatement le wsa / code de l'URL après l'atterrissage : history.replaceState, pour éviter que l'utilisateur ne partage le jeton en copiant l'URL.
  8. Établir une session propre : échangez contre la session/cookie propre à l'application d'extension ; les requêtes suivantes ne doivent plus dépendre du wsa — il expire au bout de 5 minutes.

Le verifyWsa du SDK officiel exige la présence de exp, vérifie la dérive future de iat/nbf, effectue une comparaison de signature à temps constant et applique une liste blanche d'algorithmes (par défaut uniquement HS256, ce qui élimine les attaques de confusion d'alg). En l'utilisant, vous satisfaites automatiquement les points 1 à 5.


Vérification recommandée avec le SDK

import { verifyWsa, WsaVerificationError } from '@gptbots/workspace-extension-verify'; try { const id = verifyWsa(wsa, { secret: process.env.EXTENSION_APP_SECRET, audience: 'app.example.com', }); // id: { accountId, role, workspaceId, username?, email?, avatar?, appName?, issuedAt?, expiresAt? } } catch (e) { if (e instanceof WsaVerificationError) { // e.code: InvalidToken | InvalidSignature | Expired | NotYetValid // | WrongIssuer | WrongAudience | MissingClaim | UnsupportedAlgorithm } }
                      
                      import { verifyWsa, WsaVerificationError } from '@gptbots/workspace-extension-verify';

try {
  const id = verifyWsa(wsa, {
    secret: process.env.EXTENSION_APP_SECRET,
    audience: 'app.example.com',
  });
  // id: { accountId, role, workspaceId, username?, email?, avatar?, appName?, issuedAt?, expiresAt? }
} catch (e) {
  if (e instanceof WsaVerificationError) {
    // e.code: InvalidToken | InvalidSignature | Expired | NotYetValid
    //       | WrongIssuer | WrongAudience | MissingClaim | UnsupportedAlgorithm
  }
}

                    
Ce bloc de code dans la fenêtre flottante

Options complètes et codes d'erreur : voir Référence de l'API SDK.

Vérification sans le SDK (multi-langage)

Le wsa est un JWT HS256 standard, vérifiable par n'importe quelle bibliothèque JWT. Activez impérativement la vérification explicite de iss / aud et verrouillez algorithms=['HS256'].

Java (auth0 java-jwt)

JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SHARED_SECRET)) .withIssuer("gptbots-workspace") .withAudience("app.example.com") .acceptLeeway(30) // tolère 30s de dérive d'horloge .build(); DecodedJWT jwt = verifier.verify(wsaParam); String userId = jwt.getSubject(); String workspaceId = jwt.getClaim("workspace_id").asString(); String role = jwt.getClaim("role").asString(); String username = jwt.getClaim("username").asString(); // peut être null String email = jwt.getClaim("email").asString(); // peut être null
                      
                      JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SHARED_SECRET))
    .withIssuer("gptbots-workspace")
    .withAudience("app.example.com")
    .acceptLeeway(30) // tolère 30s de dérive d'horloge
    .build();

DecodedJWT jwt = verifier.verify(wsaParam);
String userId      = jwt.getSubject();
String workspaceId = jwt.getClaim("workspace_id").asString();
String role        = jwt.getClaim("role").asString();
String username    = jwt.getClaim("username").asString();  // peut être null
String email       = jwt.getClaim("email").asString();     // peut être null

                    
Ce bloc de code dans la fenêtre flottante

Node.js (jsonwebtoken)

const jwt = require('jsonwebtoken'); const payload = jwt.verify(wsaParam, SHARED_SECRET, { algorithms: ['HS256'], issuer: 'gptbots-workspace', audience: 'app.example.com', clockTolerance: 30, }); const { sub: userId, workspace_id, role, username, email, avatar } = payload;
                      
                      const jwt = require('jsonwebtoken');

const payload = jwt.verify(wsaParam, SHARED_SECRET, {
  algorithms: ['HS256'],
  issuer: 'gptbots-workspace',
  audience: 'app.example.com',
  clockTolerance: 30,
});
const { sub: userId, workspace_id, role, username, email, avatar } = payload;

                    
Ce bloc de code dans la fenêtre flottante

Python (PyJWT)

import jwt payload = jwt.decode( wsa_param, SHARED_SECRET, algorithms=["HS256"], issuer="gptbots-workspace", audience="app.example.com", leeway=30, ) user_id = payload["sub"] workspace_id = payload["workspace_id"] role = payload["role"]
                      
                      import jwt

payload = jwt.decode(
    wsa_param,
    SHARED_SECRET,
    algorithms=["HS256"],
    issuer="gptbots-workspace",
    audience="app.example.com",
    leeway=30,
)
user_id      = payload["sub"]
workspace_id = payload["workspace_id"]
role         = payload["role"]

                    
Ce bloc de code dans la fenêtre flottante

Rappel sur l'encodage de la clé : HS256 utilise directement la chaîne de la clé, en octets UTF-8, comme clé HMAC. La clé per-app Tier 2 a la forme wext_+64 hex ; passez-la telle quelle en chaîne comme clé (ne faites pas de décodage hex), pour rester cohérent avec le côté signature de la plateforme.

Exigences de sécurité (à appliquer point par point)

  1. La clé constitue tout le modèle de sécurité. Actuellement, HS256 est une clé symétrique : en cas de fuite, n'importe qui peut falsifier l'identité de n'importe quel utilisateur d'espace de travail et attaquer l'application d'extension intégrée. À garder uniquement dans le backend (variables d'environnement/KMS), à ne jamais écrire dans le code frontend, les dépôts git, les logs ou la configuration client.
  2. Surface d'exposition du JWT dans l'URL (push uniquement) : la query string est enregistrée dans l'historique du navigateur, les access logs du serveur web, les logs de cache CDN, les enregistrements de Referer. Même enregistré, il reste exploitable s'il est récupéré dans les 5 minutes. Effacez immédiatement avec history.replaceState après l'atterrissage (déjà fait par défaut par consumeHandoff). Le mode pull (M-Auth) ne met par nature pas le wsa dans l'URL, ce qui est plus sûr.
  3. Ne transmettez pas le wsa aux sous-ressources. Une fois échangé contre une session propre, les XHR/fetch/img suivants ne portent plus le wsa d'origine, sinon il apparaîtrait dans le Referer de chaque sous-ressource.
  4. Synchronisation d'horloge. HS256 juge strictement l'exp ; l'horloge du serveur doit être synchronisée par NTP ; dans l'exemple, la leeway de 30s tolère une faible dérive, ne l'étendez pas jusqu'à la minute.
  5. Le role sert uniquement de mappage de permissions par défaut. Il reflète uniquement le rôle de l'utilisateur dans cet espace de travail, non ses permissions dans votre application ; votre application maintient son propre modèle de permissions.
  6. Isolation multi-tenant. Utilisez toujours le workspace_id comme clé d'isolation des données, pour empêcher qu'un utilisateur de l'espace de travail A ne lise les données de B.
  7. Procédure d'urgence de rotation/révocation de clé : dans la gestion de l'espace, effectuez une « rotation de clé » sur l'application, ce qui génère une nouvelle clé affichée une seule fois, puis reconfigurez votre backend avec la nouvelle clé.

5. Frontière de confiance du frontend (important)

  • Le frontend ne fait que de l'affichage réservé, pas de décision d'autorisation. Le frontend peut décoder en base64 le payload du JWT, mais sans la clé, il ne peut pas vérifier la signature — n'importe qui peut falsifier un payload « qui a l'air correct ».
  • Toute décision du type « qui est cette personne, peut-elle faire quelque chose » doit reposer sur le résultat de verifyWsa côté backend de l'application d'extension.
  • La stratégie standard est donc : le frontend remet le wsa/code au backend de l'application d'extension → le backend vérifie → le backend établit une session → le frontend ne reconnaît que cette session.

Étape suivante : Référence de l'API SDK pour consulter l'API complète ; en cas de problème, consultez Questions fréquentes et dépannage.