WebsitePlatform Login

Identity Forwarding

Delegated authentication context propagation to third-party systems via meinGPT JWT

JWT Identity Forwarding

With JWT Identity Forwarding, meinGPT passes a signed JSON Web Token to third-party systems so they can securely identify the current user and organization.

This pattern is used in two areas:

  • Custom MCP Servers — token in the X-meinGPT-JWT HTTP header
  • Custom AI Apps (iframe) — token in the URL hash fragment

JWT tokens are only issued for users with admin or member roles. Viewers do not receive tokens.

Token details

PropertyValue
AlgorithmRS256 (RSA + SHA-256)
Lifetime1 hour
HeaderIncludes kid (key ID) for key rotation
IssuerYour meinGPT instance origin (e.g. https://app.meingpt.com)

Claims reference

ClaimTypeDescription
issstringIssuer — the platform origin (e.g. https://app.meingpt.com)
substringSubject — the user's ID
audstringAudience — the organization ID
expnumberExpiration time (Unix timestamp, 1 hour from issue)
iatnumberIssued at (Unix timestamp)
jtistringUnique token ID (UUID, changes on every issue)
emailstringUser's email address
usernamestringUser's display name
organizationNamestringOrganization name
rolestring"admin" or "member"
teamsarrayTeams the user belongs to — each entry has id and name

Example payload:

{
  "iss": "https://app.meingpt.com",
  "sub": "clx1234567890",
  "aud": "clx0987654321",
  "exp": 1719504000,
  "iat": 1719500400,
  "jti": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "email": "max@example.com",
  "username": "Max Mustermann",
  "organizationName": "Acme Corp",
  "role": "admin",
  "teams": [
    { "id": "clxteam001", "name": "Engineering" }
  ]
}

Verification via JWKS

Public keys are available per organization:

https://app.meingpt.com/api/custom-apps/v1/jwks/{organizationId}
import { createRemoteJWKSet, jwtVerify } from 'jose';

const JWKS = createRemoteJWKSet(
  new URL('https://app.meingpt.com/api/custom-apps/v1/jwks/<organizationId>')
);

async function verifyMeinGptJwt(token: string) {
  const { payload } = await jwtVerify(token, JWKS, {
    issuer: 'https://app.meingpt.com',
    audience: '<organizationId>',
  });

  console.log('User:', payload.email);
  console.log('Role:', payload.role);
  return payload;
}
import jwt
from jwt import PyJWKClient

JWKS_URL = "https://app.meingpt.com/api/custom-apps/v1/jwks/<organizationId>"
jwks_client = PyJWKClient(JWKS_URL)

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

Transport: Custom MCP Servers

When JWT Identity Forwarding is enabled for an MCP server, every request includes:

X-meinGPT-JWT: eyJhbGciOiJSUzI1NiIs...

Tokens are cached within a chat session and automatically refreshed 30 seconds before expiry. Your server does not need to request new tokens — each MCP call arrives with a valid one.

How to enable

Navigate to your assistant's configuration and open the Tools section.

Add a new Custom MCP Server or edit an existing one.

Toggle JWT Authentication on. The hint text confirms: "Sends a signed meinGPT JWT in header X-meinGPT-JWT."

Reading the token on your server

Extract the header and verify it:

// Express middleware example
import { createRemoteJWKSet, jwtVerify } from 'jose';

const JWKS = createRemoteJWKSet(
  new URL('https://app.meingpt.com/api/custom-apps/v1/jwks/<organizationId>')
);

app.use(async (req, res, next) => {
  const token = req.headers['x-meingpt-jwt'];
  if (!token) return res.status(401).json({ error: 'Missing JWT' });

  try {
    const { payload } = await jwtVerify(token, JWKS, {
      issuer: 'https://app.meingpt.com',
      audience: '<organizationId>',
    });
    req.user = payload;
    next();
  } catch {
    res.status(401).json({ error: 'Invalid token' });
  }
});

Custom headers with JWT placeholder

As an alternative to the toggle, you can use the placeholder {{MEINGPT_JWT}} in any custom header value. This is useful when your server expects the token in a different header (e.g. Authorization: Bearer {{MEINGPT_JWT}}). The placeholder is replaced at runtime with the signed JWT.


Transport: Custom AI Apps (iframe)

For embedded AI Apps, the token is passed in the URL hash fragment on initial load and refreshed via postMessage.

Initial token

When meinGPT loads your app in an iframe, the URL looks like:

https://your-app.example.com/path#token=eyJhbGciOiJSUzI1NiIs...

Extract it in your frontend:

function getInitialToken() {
  const params = new URLSearchParams(window.location.hash.slice(1));
  return params.get('token');
}

Token refresh via postMessage

Tokens expire after 1 hour. Your app can request a fresh token at any time using postMessage:

1. Request a new token — send to the parent window:

window.parent.postMessage({ type: 'MEINGPT_TOKEN_REFRESH' }, '*');

2. Listen for the response:

window.addEventListener('message', (event) => {
  // Validate origin
  if (event.origin !== 'https://app.meingpt.com') return;

  if (event.data.type === 'MEINGPT_TOKEN_RESPONSE') {
    // Success — store the new token
    const { token, expiresAt } = event.data;
    setToken(token);
    scheduleRefresh(new Date(expiresAt));
  }

  if (event.data.type === 'MEINGPT_TOKEN_ERROR') {
    // Token refresh failed
    console.error('Token refresh failed:', event.data.error);
  }
});

Message types:

MessageDirectionFieldsDescription
MEINGPT_TOKEN_REFRESHApp → meinGPTRequest a fresh token
MEINGPT_TOKEN_RESPONSEmeinGPT → Apptoken, expiresAtNew signed JWT + expiry timestamp
MEINGPT_TOKEN_ERRORmeinGPT → ApperrorHuman-readable error string

meinGPT validates the postMessage origin against your configured embed URL. Messages from unexpected origins are ignored.

Full lifecycle example

let currentToken = null;
let refreshTimer = null;

// 1. Read initial token from URL hash
currentToken = new URLSearchParams(window.location.hash.slice(1)).get('token');

// 2. Schedule refresh (e.g. 5 minutes before expiry)
function scheduleRefresh(expiresAt) {
  clearTimeout(refreshTimer);
  const ms = new Date(expiresAt).getTime() - Date.now() - 5 * 60 * 1000;
  if (ms > 0) {
    refreshTimer = setTimeout(requestRefresh, ms);
  }
}

// 3. Request refresh via postMessage
function requestRefresh() {
  window.parent.postMessage({ type: 'MEINGPT_TOKEN_REFRESH' }, '*');
}

// 4. Handle response
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://app.meingpt.com') return;

  if (event.data.type === 'MEINGPT_TOKEN_RESPONSE') {
    currentToken = event.data.token;
    scheduleRefresh(event.data.expiresAt);
  }
});

Security considerations

Always verify the JWT signature via the JWKS endpoint. Never trust the token payload without cryptographic verification.

  • Verify the signature — use the JWKS endpoint, don't just base64-decode the payload
  • Check exp — reject expired tokens
  • Validate aud — ensure the audience matches your expected organization ID
  • Use HTTPS — your endpoint must use TLS to protect the token in transit
  • Don't log tokens — JWTs contain user PII (email, name); avoid writing them to application logs
  • iframe apps: validate event.origin — only accept postMessage events from your meinGPT instance origin

On this page