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

  1. Send the exact identityToken string from ASAuthorizationAppleIDCredential.identityToken (iOS) or the id_token issued by Apple (web flow)
  2. Log (server-side) the token shape on failure — part count, length — to confirm the client is sending the right credential field
  3. 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

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

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/a94e93797e4ece4f. Report an issue: GitHub.