{"record":{"id":"a94e93797e4ece4f","repo":"RocketChat/Rocket.Chat","slug":"malformed-identitytoken-jwt-must-have-3-parts","errorCode":null,"errorMessage":"Malformed identityToken: JWT must have 3 parts","messagePattern":"Malformed identityToken: JWT must have 3 parts","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts","lineNumber":145,"sourceCode":"\t\tconst isSignatureValid = verify(\n\t\t\t'RSA-SHA256',\n\t\t\tBuffer.from(`${headerB64}.${payloadB64}`),\n\t\t\tpublicKey,\n\t\t\tBuffer.from(signatureB64, 'base64url'),\n\t\t);\n\n\t\treturn isSignatureValid ? payload : null;\n\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};","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts#L127-L163","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom mobile/web clients wiring ASAuthorizationAppleIDCredential incorrectly; middleware that URL-decodes or trims the token badly; testing with placeholder strings.","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"],"exampleFix":"// iOS client - before\nlet token = authorizationCode // wrong credential\n\n// after\nlet token = String(data: credential.identityToken!, encoding: .utf8)!","handlingStrategy":"validation","validationCode":"const isCompactJws = (token: string): boolean => token.split('.').length === 3;\n\nif (!isCompactJws(identityToken)) {\n\tthrow new Error('identityToken must be a JWT (header.payload.signature)');\n}\nawait handleIdentityToken(identityToken, clientId);","typeGuard":"const isCompactJws = (token: unknown): token is string =>\n\ttypeof token === 'string' && token.split('.').length === 3 && token.length > 0;","tryCatchPattern":"try {\n\tawait handleIdentityToken(identityToken, clientId);\n} catch (e) {\n\tif (e instanceof Error && e.message.includes('must have 3 parts')) {\n\t\t// client sent the wrong credential field; fix the caller, do not retry the same token\n\t} else throw e;\n}","preventionTips":["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"],"tags":["apple-oauth","jwt","validation","sign-in-with-apple"],"backgroundTag":"malformed-jwt","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}