logo
Entwicklung
Suchen
05 · Token-Prüfung und Sicherheit

05 · Token-Prüfung und Sicherheit

Ob Push oder Pull – am Ende erhalten Entwickler:innen stets ein wsa (HS256-JWT). Dieser Artikel ist Pflichtlektüre für alle Integrator:innen – eine unzureichende Prüfung = Identität ist fälschbar.

Verpflichtende Prüf-Checkliste

Nach dem Erhalt des wsa prüfen Sie im Backend-Dienst der Erweiterungs-App der Reihe nach (verifyWsa hat alles integriert):

  1. Signatur: Mit dem von der Plattform verteilten/bei der Registrierung erhaltenen Schlüssel per HS256 prüfen. Fehlschlag → ablehnen.
  2. exp: Aktuelle Zeit ≤ exp (inkl. geringer Leeway). exp muss vorhanden sein – ein Token ohne exp sollte direkt abgelehnt werden (andernfalls läuft es nie ab).
  3. iss: Muss gleich gptbots-workspace sein.
  4. aud: Muss gleich dem Host der Ziel-Erweiterungs-App sein. Dies ist die entscheidende Abwehrlinie gegen „Token wird gestohlen und gegen eine andere App eingesetzt" und darf nicht weggelassen werden.
  5. iat / nbf (falls vorhanden): Dürfen nicht in der Zukunft liegen (über die Leeway hinaus), um zu verhindern, dass ein Token mit „Ausstellungszeitpunkt in ferner Zukunft" langfristig gültig ist.
  6. Mandantentrennung per workspace_id: Hat die Erweiterung eine Isolation über mehrere Workspaces umgesetzt, ordnen Sie die Anfrage diesem workspace_id zu und verbieten Sie mandantenübergreifende Zugriffe.
  7. Nach dem Landen sofort das wsa / code aus der URL entfernen: history.replaceState, um zu vermeiden, dass Nutzer:innen beim Kopieren der URL das Token mitteilen.
  8. Eigene Session aufbauen: In eine app-eigene Session/Cookie umwandeln; nachfolgende Anfragen sollten sich nicht mehr auf das wsa stützen – es läuft nach 5 Minuten ab.

Das verifyWsa des offiziellen SDK verlangt zwingend das Vorhandensein von exp, prüft iat/nbf auf Zukunftsdrift und führt einen Signaturvergleich in konstanter Zeit sowie eine Algorithmus-Whitelist durch (standardmäßig nur HS256, um alg-Verwirrungsangriffe auszuschließen). Wer es verwendet, erfüllt automatisch 1–5.


Prüfung mit dem SDK empfohlen

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

                    
Dieser Codeblock im schwebenden Fenster

Vollständige Optionen und Fehlercodes siehe SDK-API-Referenz.

Prüfung ohne SDK (mehrere Sprachen)

Das wsa ist ein standardmäßiges HS256-JWT, das jede JWT-Bibliothek prüfen kann. Aktivieren Sie unbedingt explizit die iss- / aud-Prüfung und fixieren Sie algorithms=['HS256'].

Java (auth0 java-jwt)

JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SHARED_SECRET)) .withIssuer("gptbots-workspace") .withAudience("app.example.com") .acceptLeeway(30) // 30s Uhrenabweichung tolerieren .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(); // kann null sein String email = jwt.getClaim("email").asString(); // kann null sein
                      
                      JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SHARED_SECRET))
    .withIssuer("gptbots-workspace")
    .withAudience("app.example.com")
    .acceptLeeway(30) // 30s Uhrenabweichung tolerieren
    .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();  // kann null sein
String email       = jwt.getClaim("email").asString();     // kann null sein

                    
Dieser Codeblock im schwebenden Fenster

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;

                    
Dieser Codeblock im schwebenden Fenster

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"]

                    
Dieser Codeblock im schwebenden Fenster

Hinweis zur Schlüsselkodierung: HS256 verwendet die Schlüsselzeichenkette direkt als UTF-8-Bytes als HMAC-Key. Der per-App-Schlüssel von Tier 2 hat die Form wext_+64 hex; übergeben Sie ihn bitte als unveränderte Zeichenkette als Schlüssel (keine erneute Hex-Dekodierung), konsistent zur Signaturseite der Plattform.

Sicherheitsanforderungen (Punkt für Punkt umsetzen)

  1. Der Schlüssel ist das gesamte Sicherheitsmodell. Das aktuelle HS256 ist ein symmetrischer Schlüssel: Einmal geleakt, kann jede:r beliebige Workspace-Nutzeridentitäten fälschen und in die angebundene Erweiterungs-App eindringen. Nur im Backend halten (Umgebungsvariablen/KMS), niemals in Frontend-Code, Git-Repository, Logs oder Client-Konfiguration schreiben.
  2. Exposition des JWT in der URL (nur Push): Der Query-String wird in Browser-Verlauf, Access-Logs von Webservern, CDN-Cache-Logs und Referer-Einträgen erfasst. Selbst wenn erfasst, ist es innerhalb von 5 Minuten weiterhin nutzbar. Nach dem Landen sofort mit history.replaceState entfernen (consumeHandoff macht dies standardmäßig). Pull (M-Auth) legt das wsa von Natur aus nicht in die URL und ist sicherer.
  3. Das wsa nicht an Unterressourcen weiterreichen. Nach dem Umwandeln in die eigene Session tragen nachfolgende XHR/fetch/img das rohe wsa grundsätzlich nicht mehr mit, sonst erscheint es im Referer jeder Unterressource.
  4. Uhrensynchronisation. HS256 bewertet exp streng; die Serveruhr muss per NTP synchronisiert sein; im Beispiel toleriert die 30s-leeway eine geringe Abweichung, weiten Sie sie nicht auf Minuten aus.
  5. role nur als Standard-Berechtigungszuordnung. Es spiegelt nur die Rolle der Nutzer:innen in diesem Workspace wider, nicht die Berechtigung innerhalb Ihrer App; Ihre App pflegt ihr eigenes Berechtigungsmodell.
  6. Mandantentrennung. Verwenden Sie stets workspace_id als Datentrennungsschlüssel, um zu verhindern, dass Nutzer:innen aus Workspace A Daten von B lesen.
  7. Notfall bei Schlüsselrotation/-widerruf: „Schlüssel rotieren" für die App in der Space-Verwaltung – es wird ein neuer Schlüssel generiert und einmalig angezeigt; konfigurieren Sie anschließend Ihr Backend mit dem neuen Schlüssel neu.

5. Vertrauensgrenze des Frontends (wichtig)

  • Das Frontend dient nur als Anzeige-Platzhalter, nicht für Autorisierungsentscheidungen. Das Frontend kann den Payload des JWT per base64 öffnen, aber ohne Schlüssel kann die Signatur nicht geprüft werden – jede:r kann einen „scheinbar korrekten" Payload fälschen.
  • Alle Entscheidungen darüber, „wer diese Person ist und ob sie etwas tun darf", müssen sich nach dem Ergebnis von verifyWsa im Backend der Erweiterungs-App richten.
  • Die Standardstrategie lautet daher: Das Frontend übergibt das wsa/code an das Backend der Erweiterungs-App → das Backend prüft → das Backend baut eine Session auf → das Frontend akzeptiert nur diese Session.

Nächster Schritt: SDK-API-Referenz für die vollständige API; bei Problemen konsultieren Sie bitte Fehlerbehebung.