WebsitePlatform Login

JWT-Authentifizierung für Custom Apps

Sichere Authentifizierung für Custom Apps in iframes mittels JWT-Tokens

Custom Apps, die in meinGPT über iframes eingebettet sind, können Benutzer mittels JWT (JSON Web Token) authentifizieren. Dies ermöglicht es deiner Custom App, den zugreifenden Benutzer sicher zu identifizieren und dessen Berechtigungen innerhalb der Organisation zu kennen.

Schnellstart

Token abrufen - Extrahiere ihn aus dem URL-Hash-Fragment:

const token = new URLSearchParams(window.location.hash.slice(1)).get('token');

Token verifizieren - Verwende den JWKS-Endpunkt, um die Signatur auf deinem Backend zu verifizieren (siehe Beispiele unten).

Vor Ablauf erneuern - Tokens laufen nach 1 Stunde ab. Fordere eine Erneuerung via postMessage an.

Live-Demo

Verwende das interaktive Demo-Tool zum Testen der JWT-Verifizierung:

https://jwt-demo-iframe.selectcode.workers.dev

Die Demo:

  • Verifiziert Signaturen mittels Web Crypto API und JWKS-Endpunkt
  • Zeigt dekodierten Payload mit allen Claims (Benutzer, Rolle, Teams)
  • Zeigt Verifizierungsstatus (gültige/ungültige Signatur)
  • Liefert Code-Beispiele für Node.js, Python und Browser

Wenn die Demo in meinGPT eingebettet ist, empfängt und verifiziert sie automatisch den JWT-Token. Direkter Zugriff mit Token: https://jwt-demo-iframe.selectcode.workers.dev#token=eyJ...

Wichtige URLs

ZweckURL
JWKS-Endpunkthttps://app.meingpt.com/api/custom-apps/v1/jwks/{organizationId}
Demo-Toolhttps://jwt-demo-iframe.selectcode.workers.dev

Übersicht

Wenn ein Benutzer eine Custom App in meinGPT öffnet:

  1. meinGPT generiert einen signierten JWT-Token mit Benutzerinformationen
  2. Der Token wird über das URL-Fragment (Hash) an deine App übergeben
  3. Deine App verifiziert den Token mit dem öffentlichen Schlüssel der Organisation
  4. Der Token läuft nach 1 Stunde ab und kann per postMessage erneuert werden

Token-Struktur

Der JWT-Token enthält folgende Claims:

ClaimTypBeschreibung
issstringIssuer, immer https://app.meingpt.com
substringSubject - die eindeutige Benutzer-ID
audstringAudience - die Organisations-ID
expnumberAblaufzeit (Unix-Zeitstempel)
iatnumberAusstellungszeit (Unix-Zeitstempel)
jtistringEindeutige Token-ID
emailstringE-Mail-Adresse des Benutzers
rolestringRolle des Benutzers: admin oder member
teamsarrayTeams, denen der Benutzer angehört (ID und Name)

Beispiel-Payload

{
  "iss": "https://app.meingpt.com",
  "sub": "user_abc123",
  "aud": "org_xyz789",
  "exp": 1704067200,
  "iat": 1704063600,
  "jti": "550e8400-e29b-41d4-a716-446655440000",
  "email": "benutzer@beispiel.de",
  "role": "admin",
  "teams": [
    { "id": "dept_001", "name": "Entwicklung" },
    { "id": "dept_002", "name": "Produkt" }
  ]
}

Token empfangen

Der JWT-Token wird über das URL-Fragment (Hash) an deine App übergeben, welches nicht an deinen Server gesendet wird:

https://deine-app.de/embed#token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

Token extrahieren (JavaScript)

// Token aus URL-Fragment extrahieren
function getTokenFromUrl() {
  const hash = window.location.hash.substring(1); // '#' entfernen
  const params = new URLSearchParams(hash);
  return params.get('token');
}

const token = getTokenFromUrl();
if (token) {
  // Token verifizieren und verwenden
  const payload = await verifyToken(token);
  console.log('Benutzer:', payload.email);
  console.log('Rolle:', payload.role);
}

Token verifizieren

Tokens werden mit RS256 (RSA-SHA256) signiert. Du solltest Tokens mit dem öffentlichen Schlüssel der Organisation verifizieren, der über den JWKS-Endpunkt verfügbar ist.

JWKS-Endpunkt

GET /api/custom-apps/v1/jwks/{organizationId}

Antwort:

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "alg": "RS256",
      "kid": "key_abc123...",
      "n": "...",
      "e": "AQAB"
    }
  ]
}

Verifizierung mit Node.js

import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

const client = jwksClient({
  jwksUri: `https://app.meingpt.com/api/custom-apps/v1/jwks/${organizationId}`
});

function getKey(header, callback) {
  client.getSigningKey(header.kid, (err, key) => {
    if (err) return callback(err);
    const signingKey = key.publicKey || key.rsaPublicKey;
    callback(null, signingKey);
  });
}

function verifyToken(token) {
  return new Promise((resolve, reject) => {
    jwt.verify(token, getKey, {
      issuer: 'https://app.meingpt.com',
      audience: organizationId,
      algorithms: ['RS256']
    }, (err, decoded) => {
      if (err) reject(err);
      else resolve(decoded);
    });
  });
}

Verifizierung mit jose (Modernes Node.js)

import * as jose from 'jose';

const JWKS = jose.createRemoteJWKSet(
  new URL(`https://app.meingpt.com/api/custom-apps/v1/jwks/${organizationId}`)
);

async function verifyToken(token) {
  const { payload, protectedHeader } = await jose.jwtVerify(token, JWKS, {
    issuer: 'https://app.meingpt.com',
    audience: organizationId,
  });
  return payload;
}

// Verwendung
const payload = await verifyToken(token);
console.log('Benutzer:', payload.email);
console.log('Rolle:', payload.role);

Verifizierung im Browser / Edge Functions (Web Crypto API)

Für client-seitige oder Edge-Runtime-Verifizierung ohne externe Abhängigkeiten:

// Base64url zu Uint8Array dekodieren
function base64UrlDecode(str) {
  const pad = str.length % 4;
  const padded = pad ? str + '='.repeat(4 - pad) : str;
  const base64 = padded.replace(/-/g, '+').replace(/_/g, '/');
  const binary = atob(base64);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes;
}

// JWKS abrufen und Key nach kid finden
async function getPublicKey(issuer, organizationId, kid) {
  const jwksUrl = `${issuer}/api/custom-apps/v1/jwks/${organizationId}`;
  const response = await fetch(jwksUrl);
  const jwks = await response.json();
  const jwk = jwks.keys.find(k => k.kid === kid);

  return crypto.subtle.importKey(
    'jwk',
    { kty: jwk.kty, n: jwk.n, e: jwk.e, alg: 'RS256' },
    { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
    false,
    ['verify']
  );
}

// JWT-Signatur verifizieren
async function verifyToken(token) {
  const [headerB64, payloadB64, signatureB64] = token.split('.');
  const header = JSON.parse(atob(headerB64.replace(/-/g, '+').replace(/_/g, '/')));
  const payload = JSON.parse(atob(payloadB64.replace(/-/g, '+').replace(/_/g, '/')));

  // Public Key vom JWKS abrufen
  const cryptoKey = await getPublicKey(payload.iss, payload.aud, header.kid);

  // Signatur verifizieren
  const data = new TextEncoder().encode(headerB64 + '.' + payloadB64);
  const signature = base64UrlDecode(signatureB64);
  const valid = await crypto.subtle.verify('RSASSA-PKCS1-v1_5', cryptoKey, signature, data);

  if (!valid) throw new Error('Ungültige Signatur');
  if (payload.exp < Date.now() / 1000) throw new Error('Token abgelaufen');

  return payload;
}

Verifizierung mit Python

import jwt
from jwt import PyJWKClient

jwks_url = f"https://app.meingpt.com/api/custom-apps/v1/jwks/{organization_id}"
jwks_client = PyJWKClient(jwks_url)

def verify_token(token: str) -> dict:
    signing_key = jwks_client.get_signing_key_from_jwt(token)
    return jwt.decode(
        token,
        signing_key.key,
        algorithms=["RS256"],
        issuer="https://app.meingpt.com",
        audience=organization_id
    )

# Verwendung
payload = verify_token(token)
print(f"Benutzer: {payload['email']}")
print(f"Rolle: {payload['role']}")

Token erneuern

Tokens laufen nach 1 Stunde ab. Deine App kann einen neuen Token über die postMessage-API anfordern:

Erneuerung anfordern

// Neuen Token vom übergeordneten Fenster anfordern
function requestTokenRefresh() {
  window.parent.postMessage({ type: 'MEINGPT_TOKEN_REFRESH' }, '*');
}

// Auf Antwort warten
window.addEventListener('message', (event) => {
  // Origin validieren
  if (!event.origin.includes('meingpt.com')) return;

  if (event.data?.type === 'MEINGPT_TOKEN_RESPONSE') {
    const newToken = event.data.token;
    const expiresAt = new Date(event.data.expiresAt);
    // Neuen Token speichern und verwenden
    handleNewToken(newToken, expiresAt);
  }

  if (event.data?.type === 'MEINGPT_TOKEN_ERROR') {
    console.error('Token-Erneuerung fehlgeschlagen:', event.data.error);
  }
});

// Automatische Erneuerung vor Ablauf einrichten
function scheduleRefresh(expiresAt) {
  const refreshTime = expiresAt.getTime() - Date.now() - 5 * 60 * 1000; // 5 Min vor Ablauf
  if (refreshTime > 0) {
    setTimeout(requestTokenRefresh, refreshTime);
  }
}

Sicherheits-Best-Practices

Verifiziere Tokens immer auf deinem Backend. Vertraue Token-Inhalten niemals ohne Verifizierung.

Token-Signatur verifizieren - Verifiziere die JWT-Signatur immer über den JWKS-Endpunkt. Überspringe die Verifizierung niemals.

Claims validieren - Prüfe die Claims iss (Issuer), aud (Audience/Organisation) und exp (Ablaufzeit).

Nur HTTPS verwenden - Deine Custom App muss über HTTPS bereitgestellt werden, um Tokens sicher zu empfangen.

postMessage-Origin validieren - Validiere beim Empfang von Token-Erneuerungsantworten, dass der Message-Origin der erwarteten meinGPT-Domain entspricht.

Token-Ablauf behandeln - Implementiere eine ordnungsgemäße Token-Erneuerungslogik und behandle abgelaufene Tokens angemessen.

Fehlerbehebung

Token nicht empfangen

  • Stelle sicher, dass deine App in einem iframe innerhalb von meinGPT geladen wird
  • Prüfe, dass das Custom App-Modul eine embedUrl konfiguriert hat
  • Verifiziere, dass das URL-Fragment von deiner App erhalten bleibt

Token-Verifizierung schlägt fehl

  • Stelle sicher, dass du die korrekte Organisations-ID für den JWKS-Endpunkt verwendest
  • Prüfe, ob der Token abgelaufen ist
  • Verifiziere, dass du den RS256-Algorithmus zur Verifizierung verwendest

postMessage funktioniert nicht

  • Bestätige, dass deine App über HTTPS läuft
  • Prüfe, ob du auf die korrekten Message-Typen hörst
  • Validiere den Message-Origin zur Sicherheit

Support

Wenn du Hilfe bei der Integration der JWT-Authentifizierung benötigst, kontaktiere uns unter support@meingpt.com.