{"record":{"id":"2201213b6ee14e4a","repo":"RocketChat/Rocket.Chat","slug":"insufficient-data-missing-subject-sub-in-auth-r","errorCode":null,"errorMessage":"Insufficient data: Missing subject (sub) in auth response token","messagePattern":"Insufficient data: Missing subject \\(sub\\) in auth response token","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts","lineNumber":157,"sourceCode":"}\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":139,"sourceCodeEnd":167,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/auth-providers/apple/handleIdentityToken.ts#L139-L167","documentation":"Thrown by handleIdentityToken when the JWT passed all cryptographic and claim checks but has no sub (subject) claim, which Rocket.Chat uses as the stable Apple user id (serviceData.id = payload.sub). Genuine Apple identity tokens always carry sub, so in practice this indicates an anomalous token: a correctly-signed-but-wrong-type JWT, an access token instead of an identity token, or a manipulated payload.","triggerScenarios":"Client sends an Apple access/authorization token (valid signature/iss/aud) that simply has no sub; payload stripped of claims before submission; IdP edge case where a non-identity JWT for the same audience is reused.","commonSituations":"Confusing token types in the Apple flow docs while building a custom client; interceptors that drop JWT payload fields; replaying tokens captured from a different Apple API.","solutions":["Send the identityToken (credential.identityToken / id_token), never an access token, to the Apple OAuth endpoint","Client-side: decode the payload (middle base64url segment) and assert a non-empty sub before submitting","If building your own flow, verify with Apple docs that you request the identity token scope"],"exampleFix":"// client-side guard\nconst parts = identityToken.split('.');\nconst payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));\nif (!payload.sub) throw new Error('Not an Apple identity token');","handlingStrategy":"validation","validationCode":"function decodeJwtPayload(token: string): Record<string, unknown> {\n\tconst payload = token.split('.')[1];\n\treturn JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));\n}\n\nconst payload = decodeJwtPayload(identityToken);\nif (!payload.sub) {\n\tthrow new Error('Token has no subject — send the Apple identityToken, not an access token');\n}\nawait handleIdentityToken(identityToken, clientId);","typeGuard":"const hasSubject = (payload: Record<string, unknown>): payload is { sub: string } =>\n\ttypeof payload.sub === 'string' && payload.sub.length > 0;","tryCatchPattern":"try {\n\tawait handleIdentityToken(identityToken, clientId);\n} catch (e) {\n\tif (e instanceof Error && e.message.includes('Missing subject')) {\n\t\t// wrong token type from the client — request the real identityToken\n\t} else throw e;\n}","preventionTips":["Use only Apple's identity token (id_token) for login; never access/authorization tokens","In QA pipelines, generate tokens via Apple's flows rather than hand-built JWTs","Client-side: assert the decoded payload has sub before calling the server"],"tags":["apple-oauth","jwt","missing-claim","sign-in-with-apple"],"backgroundTag":"jwt-missing-claim","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"}