RocketChat/Rocket.Chat · error · Error

identityToken is not a valid Apple JWT or has expired

Error message

identityToken is not a valid Apple JWT or has expired

What it means

Thrown by handleIdentityToken when verifyAppleJWT returns null. Verification comprises: exp not passed, iss must be https://appleid.apple.com, aud must include one of the configured clientIds (plus the built-in chat.rocket.ios audience), a JWKS key matching the token's kid must exist, and the RSA-SHA256 signature must be valid. Any single failure yields null, and this error reports it without saying which check failed — check the server console, where each failure logs a specific message.

Source

Thrown at apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts:153

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

	return serviceData;
}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Match the server console error ('Apple JWT has expired', 'Invalid audience...', 'Matching Key ID (kid) not found', signature failure) to the failing check
  2. If audience: configure the exact Apple Services ID (web) / bundle ID (native) in the Apple OAuth settings so it lands in the clientId list checked against aud
  3. If expired: submit the identityToken immediately after authentication and sync server time (NTP)
  4. If kid/signature: ensure the token comes from a genuine Apple sign-in, not a hand-built or other-provider JWT
Defensive patterns

Strategy: try-catch

Try / catch

try {
	const serviceData = await handleIdentityToken(identityToken, clientId);
} catch (e) {
	if (e instanceof Error && e.message.includes('not a valid Apple JWT')) {
		// check server console for the specific failing check (expired / issuer / audience / kid / signature)
		// common fixes: fresh token, correct Services ID in AAPL settings, synced clock
	} else throw e;
}

Prevention

When it happens

Trigger: Token expired (Apple identity tokens are short-lived ~10 min, often replayed late or with server clock skew); aud mismatch because the Apple Services ID / bundle ID configured in Rocket.Chat (Accounts AAPL id) differs from the audience in the token; kid not in Apple's JWKS (token from another IdP or stale keys); signature invalid (tampered token).

Common situations: Wrong 'client id' configured for the Apple OAuth app (using the app ID where the Services ID is required, or vice versa); delayed token submission (queued/retried requests); server clock drift; testing with old captured tokens.

Related errors


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