RocketChat/Rocket.Chat · error · Error
Malformed identityToken: JWT must have 3 parts
Error message
Malformed identityToken: JWT must have 3 parts
What it means
Thrown by handleIdentityToken when the supplied identityToken does not split on '.' into exactly three parts, i.e., it is not a JWS compact serialization (header.payload.signature). Apple identity tokens are JWTs; anything else — an authorization code, an opaque blob, an HTML error page — fails this structural check before any crypto runs.
Source
Thrown at apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts:145
const isSignatureValid = verify(
'RSA-SHA256',
Buffer.from(`${headerB64}.${payloadB64}`),
publicKey,
Buffer.from(signatureB64, 'base64url'),
);
return isSignatureValid ? payload : null;
} catch (error) {
console.error('Cryptographic signature verification failed:', error);
return null;
}
}
export async function handleIdentityToken(identityToken: string, clientId: string): Promise<Record<string, any>> {
const parts = identityToken.split('.');
if (parts.length !== 3) {
throw new Error('Malformed identityToken: JWT must have 3 parts');
}
const [headerB64, payloadB64, signatureB64] = parts;
const payload = await verifyAppleJWT(headerB64, payloadB64, signatureB64, clientId);
if (!payload) {
throw new Error('identityToken is not a valid Apple JWT or has expired');
}
if (!payload.sub) {
throw new Error('Insufficient data: Missing subject (sub) in auth response token');
}
const serviceData = {
id: payload.sub,
...payload,
};View on GitHub (pinned to b2c16d5842)
Solutions
- Send the exact identityToken string from ASAuthorizationAppleIDCredential.identityToken (iOS) or the id_token issued by Apple (web flow)
- Log (server-side) the token shape on failure — part count, length — to confirm the client is sending the right credential field
- Add a client-side sanity check: token must contain exactly two '.' characters
Example fix
// iOS client - before let token = authorizationCode // wrong credential // after let token = String(data: credential.identityToken!, encoding: .utf8)!
Defensive patterns
Strategy: validation
Validate before calling
const isCompactJws = (token: string): boolean => token.split('.').length === 3;
if (!isCompactJws(identityToken)) {
throw new Error('identityToken must be a JWT (header.payload.signature)');
}
await handleIdentityToken(identityToken, clientId); Type guard
const isCompactJws = (token: unknown): token is string =>
typeof token === 'string' && token.split('.').length === 3 && token.length > 0; Try / catch
try {
await handleIdentityToken(identityToken, clientId);
} catch (e) {
if (e instanceof Error && e.message.includes('must have 3 parts')) {
// client sent the wrong credential field; fix the caller, do not retry the same token
} else throw e;
} Prevention
- Always pass ASAuthorizationAppleIDCredential.identityToken (or Apple's id_token), never the authorization code
- Never log/truncate/transform the token between client and server
- Add a client-side two-dots sanity check before submitting
When it happens
Trigger: Apple OAuth callback posting the wrong field (e.g., the authorization code or id_token from a different flow) as identityToken; client truncating the token; token containing extra/missing dots; forwarding a base64-encoded whole payload instead of the JWT.
Common situations: Custom mobile/web clients wiring ASAuthorizationAppleIDCredential incorrectly; middleware that URL-decodes or trims the token badly; testing with placeholder strings.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- identityToken is not a valid Apple JWT or has expired
- Insufficient data: Missing subject (sub) in auth response to
- Could not retrieve Apple public keys
- Invalid reaction
- Invalid message id
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/a94e93797e4ece4f.
Report an issue: GitHub.