{"record":{"id":"58fe2dfdaf1dde1d","repo":"mastra-ai/mastra","slug":"google-token-response-did-not-include-an-id-token","errorCode":null,"errorMessage":"Google token response did not include an ID token","messagePattern":"Google token response did not include an ID token","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"auth/google/src/auth-provider.ts","lineNumber":502,"sourceCode":"        }),\n        signal: AbortSignal.timeout(10_000),\n      });\n\n      if (!tokenResponse.ok) {\n        const error = await tokenResponse.text();\n        throw new Error(`Google token exchange failed: ${error}`);\n      }\n\n      const tokens = (await tokenResponse.json()) as {\n        access_token: string;\n        id_token?: string;\n        refresh_token?: string;\n        expires_in: number;\n        token_type: string;\n      };\n\n      if (!tokens.id_token) {\n        throw new Error('Google token response did not include an ID token');\n      }\n\n      const user = await self.verifyIdToken(tokens.id_token, nonce);\n      const sessionData = {\n        user,\n        expiresAt: Date.now() + self.cookieMaxAge * 1000,\n      };\n      const encryptedSession = await encryptSession(sessionData, self.cookiePassword);\n      const cookieValue = `${self.cookieName}=${encodeURIComponent(encryptedSession)}; ${self.cookieFlags(self.cookieMaxAge)}`;\n\n      return {\n        user,\n        tokens: {\n          accessToken: tokens.access_token,\n          refreshToken: tokens.refresh_token,\n          idToken: tokens.id_token,\n          expiresAt: new Date(Date.now() + tokens.expires_in * 1000),\n        },","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/auth/google/src/auth-provider.ts#L484-L520","documentation":"Thrown by MastraAuthGoogle.handleCallback after a successful token exchange when Google's token response JSON has no id_token field. This provider requires an OIDC ID token (it verifies it with verifyIdToken and the nonce) to build the user session, so a bare OAuth2 access-token response is unusable.","triggerScenarios":"handleCallback receives a 200 response whose body lacks id_token — typically because the original authorization request did not include the openid scope (or email/profile scopes), or a non-OIDC client configuration returned only an access token.","commonSituations":"Scopes configured without 'openid' (custom self.scopes overriding defaults); an admin changed scopes to only drive/calendar etc.; testing against a mocked token endpoint that omits id_token; Google account flows that skip OIDC consent.","solutions":["Ensure 'openid' (plus 'email' and 'profile' as needed) is included in the scopes configured on MastraAuthGoogle / GOOGLE_SCOPES.","Restart the sign-in flow — the missing id_token was fixed at the authorization step; the current code cannot be salvaged.","If using custom scopes, add openid to the list rather than replacing default OIDC scopes.","If intercepting tokens yourself, verify the token endpoint response actually contains id_token before treating the flow as complete."],"exampleFix":"// before\nnew MastraAuthGoogle({ scopes: ['email', 'profile'] });\n// after\nnew MastraAuthGoogle({ scopes: ['openid', 'email', 'profile'] });","handlingStrategy":"validation","validationCode":"const SCOPES = ['openid', 'email', 'profile'];\nif (!scopes.includes('openid')) throw new Error('Google SSO scopes must include openid so the token response contains an id_token');","typeGuard":"function hasIdToken(t: { access_token: string; id_token?: string }): t is { access_token: string; id_token: string } {\n  return typeof t.id_token === 'string' && t.id_token.length > 0;\n}","tryCatchPattern":"try {\n  const result = await provider.handleCallback(code, state);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Google token response did not include an ID token') {\n    // scopes were missing openid at authorization time — restart the flow with corrected scopes\n    return redirectTo(provider.getAuthorizationUrl(state));\n  }\n  throw err;\n}","preventionTips":["Always include 'openid' in scopes; merge custom scopes with the OIDC defaults instead of replacing them","If intercepting token responses yourself, assert id_token exists before proceeding","When mocking Google's token endpoint in tests, include id_token in fixtures","After scope changes, force users through a fresh authorization flow"],"tags":["oauth","oidc","google","sso","missing-field"],"backgroundTag":"missing-id-token","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}