{"record":{"id":"4d23914dc9b51ebc","repo":"RocketChat/Rocket.Chat","slug":"identitytoken-is-not-a-valid-apple-jwt-or-has-expi","errorCode":null,"errorMessage":"identityToken is not a valid Apple JWT or has expired","messagePattern":"identityToken is not a valid Apple JWT or has expired","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts","lineNumber":153,"sourceCode":"\t} catch (error) {\n\t\tconsole.error('Cryptographic signature verification failed:', error);\n\t\treturn null;\n\t}\n}\n\nexport async function handleIdentityToken(identityToken: string, clientId: string): Promise<Record<string, any>> {\n\tconst parts = identityToken.split('.');\n\n\tif (parts.length !== 3) {\n\t\tthrow new Error('Malformed identityToken: JWT must have 3 parts');\n\t}\n\n\tconst [headerB64, payloadB64, signatureB64] = parts;\n\n\tconst payload = await verifyAppleJWT(headerB64, payloadB64, signatureB64, clientId);\n\n\tif (!payload) {\n\t\tthrow new Error('identityToken is not a valid Apple JWT or has expired');\n\t}\n\n\tif (!payload.sub) {\n\t\tthrow new Error('Insufficient data: Missing subject (sub) in auth response token');\n\t}\n\n\tconst serviceData = {\n\t\tid: payload.sub,\n\t\t...payload,\n\t};\n\n\treturn serviceData;\n}\n","sourceCodeStart":135,"sourceCodeEnd":167,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts#L135-L167","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n\tconst serviceData = await handleIdentityToken(identityToken, clientId);\n} catch (e) {\n\tif (e instanceof Error && e.message.includes('not a valid Apple JWT')) {\n\t\t// check server console for the specific failing check (expired / issuer / audience / kid / signature)\n\t\t// common fixes: fresh token, correct Services ID in AAPL settings, synced clock\n\t} else throw e;\n}","preventionTips":["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"],"tags":["apple-oauth","jwt","audience-validation","clock-skew","sign-in-with-apple"],"backgroundTag":"jwt-validation-failed","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}