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
- Match the server console error ('Apple JWT has expired', 'Invalid audience...', 'Matching Key ID (kid) not found', signature failure) to the failing check
- 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
- If expired: submit the identityToken immediately after authentication and sync server time (NTP)
- 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
- Submit the identityToken immediately; Apple tokens expire within minutes
- Configure the exact Apple Services ID / bundle ID so the aud claim matches
- Keep server time NTP-synced and never reuse captured tokens in tests
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
- Malformed identityToken: JWT must have 3 parts
- Insufficient data: Missing subject (sub) in auth response to
- Could not retrieve Apple public keys
- [Message Delivery] High delay detected: ${receiveDelay}ms. P
- error-message-ts-out-of-sync
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/4d23914dc9b51ebc.
Report an issue: GitHub.