{"record":{"id":"7431d5103eb62a40","repo":"mastra-ai/mastra","slug":"google-id-token-is-missing-subject","errorCode":null,"errorMessage":"Google ID token is missing subject","messagePattern":"Google ID token is missing subject","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"auth/google/src/auth-provider.ts","lineNumber":369,"sourceCode":"  }\n\n  private async verifyIdToken(token: string, nonce?: string): Promise<GoogleUser> {\n    const { payload } = await jwtVerify(token, this.jwks, {\n      issuer: GOOGLE_ISSUERS,\n      audience: this.clientId,\n    });\n\n    if (nonce && payload.nonce !== nonce) {\n      throw new Error('Invalid Google ID token nonce');\n    }\n\n    if (hasExpired(payload)) {\n      throw new Error('Google ID token has expired');\n    }\n\n    const user = mapGoogleClaimsToUser(payload);\n    if (!user.googleId) {\n      throw new Error('Google ID token is missing subject');\n    }\n\n    if (!this.isHostedDomainAllowed(user.hostedDomain)) {\n      throw new Error('Google user is not in an allowed hosted domain');\n    }\n\n    return user;\n  }\n\n  private isHostedDomainAllowed(hostedDomain: string | undefined): boolean {\n    if (this.allowedDomains.length === 0) return true;\n    const domain = normalizeDomain(hostedDomain);\n    if (!domain) return false;\n    return this.allowedDomains.includes(domain);\n  }\n\n  private extractBearerToken(request: Request): string | null {\n    const authHeader = request.headers.get('Authorization');","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/auth/google/src/auth-provider.ts#L351-L387","documentation":"mapGoogleClaimsToUser maps the JWT payload to a user; the googleId field comes from the token's sub (subject) claim. If googleId is missing, the token has no subject and cannot identify a user, so verifyIdToken throws. A Google ID token without a sub claim is not a valid identity assertion.","triggerScenarios":"Calling verifyIdToken with a token whose payload lacks the sub claim (or maps to a user with a falsy googleId) — typically a malformed, hand-crafted, or non-Google token that nevertheless passes issuer/audience checks, or a misconfigured mock in tests.","commonSituations":"Testing with locally forged JWTs that omit sub; a stub/mock JWKS issuing claim sets copied incorrectly; tokens from a non-Google IdP pointed at the same issuer/audience configuration.","solutions":["Use a genuine Google-issued ID token from a real OAuth code exchange.","In tests, include sub in the mock token payload when minting tokens.","Ensure your issuer configuration points only at Google's issuer URLs so foreign tokens can't reach this code path.","Log the decoded payload (without secrets) to confirm which claims the offending token actually carries."],"exampleFix":"// before (test mock without subject)\nconst payload = { aud: clientId, iss: 'https://accounts.google.com' };\n\n// after\nconst payload = { aud: clientId, iss: 'https://accounts.google.com', sub: 'user-123', exp: ... };","handlingStrategy":"validation","validationCode":"const { payload } = decodeJwt(token);\nif (typeof payload.sub !== 'string' || payload.sub.length === 0) {\n  throw new Error('Token has no subject claim; cannot identify user');\n}","typeGuard":"function hasSubject(p: object): p is { sub: string } {\n  return 'sub' in p && typeof (p as { sub?: unknown }).sub === 'string' && (p as { sub: string }).sub.length > 0;\n}","tryCatchPattern":"try {\n  const user = await provider.verifyIdToken(token, nonce);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Google ID token is missing subject') {\n    // token is not a valid identity assertion: reject and restart flow\n    return res.redirect('/login');\n  }\n  throw err;\n}","preventionTips":["Only accept tokens from Google's real issuer endpoints; don't lower issuer checks.","In tests, always include sub (and aud/iss/exp) in mocked token payloads.","Never accept hand-crafted tokens in production code paths.","Log claim keys (not values) when debugging unexpected claim shapes."],"tags":["oauth","openid-connect","jwt","claims","token-validation"],"backgroundTag":"jwt-claim-validation-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}