mastra-ai/mastra · error

Google token response did not include an ID token

Error message

Google token response did not include an ID token

What it means

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.

Source

Thrown at auth/google/src/auth-provider.ts:502

        }),
        signal: AbortSignal.timeout(10_000),
      });

      if (!tokenResponse.ok) {
        const error = await tokenResponse.text();
        throw new Error(`Google token exchange failed: ${error}`);
      }

      const tokens = (await tokenResponse.json()) as {
        access_token: string;
        id_token?: string;
        refresh_token?: string;
        expires_in: number;
        token_type: string;
      };

      if (!tokens.id_token) {
        throw new Error('Google token response did not include an ID token');
      }

      const user = await self.verifyIdToken(tokens.id_token, nonce);
      const sessionData = {
        user,
        expiresAt: Date.now() + self.cookieMaxAge * 1000,
      };
      const encryptedSession = await encryptSession(sessionData, self.cookiePassword);
      const cookieValue = `${self.cookieName}=${encodeURIComponent(encryptedSession)}; ${self.cookieFlags(self.cookieMaxAge)}`;

      return {
        user,
        tokens: {
          accessToken: tokens.access_token,
          refreshToken: tokens.refresh_token,
          idToken: tokens.id_token,
          expiresAt: new Date(Date.now() + tokens.expires_in * 1000),
        },

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Ensure 'openid' (plus 'email' and 'profile' as needed) is included in the scopes configured on MastraAuthGoogle / GOOGLE_SCOPES.
  2. Restart the sign-in flow — the missing id_token was fixed at the authorization step; the current code cannot be salvaged.
  3. If using custom scopes, add openid to the list rather than replacing default OIDC scopes.
  4. If intercepting tokens yourself, verify the token endpoint response actually contains id_token before treating the flow as complete.

Example fix

// before
new MastraAuthGoogle({ scopes: ['email', 'profile'] });
// after
new MastraAuthGoogle({ scopes: ['openid', 'email', 'profile'] });
Defensive patterns

Strategy: validation

Validate before calling

const SCOPES = ['openid', 'email', 'profile'];
if (!scopes.includes('openid')) throw new Error('Google SSO scopes must include openid so the token response contains an id_token');

Type guard

function hasIdToken(t: { access_token: string; id_token?: string }): t is { access_token: string; id_token: string } {
  return typeof t.id_token === 'string' && t.id_token.length > 0;
}

Try / catch

try {
  const result = await provider.handleCallback(code, state);
} catch (err) {
  if (err instanceof Error && err.message === 'Google token response did not include an ID token') {
    // scopes were missing openid at authorization time — restart the flow with corrected scopes
    return redirectTo(provider.getAuthorizationUrl(state));
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/58fe2dfdaf1dde1d. Report an issue: GitHub.