mastra-ai/mastra · error

Google ID token is missing subject

Error message

Google ID token is missing subject

What it means

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.

Source

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

  }

  private async verifyIdToken(token: string, nonce?: string): Promise<GoogleUser> {
    const { payload } = await jwtVerify(token, this.jwks, {
      issuer: GOOGLE_ISSUERS,
      audience: this.clientId,
    });

    if (nonce && payload.nonce !== nonce) {
      throw new Error('Invalid Google ID token nonce');
    }

    if (hasExpired(payload)) {
      throw new Error('Google ID token has expired');
    }

    const user = mapGoogleClaimsToUser(payload);
    if (!user.googleId) {
      throw new Error('Google ID token is missing subject');
    }

    if (!this.isHostedDomainAllowed(user.hostedDomain)) {
      throw new Error('Google user is not in an allowed hosted domain');
    }

    return user;
  }

  private isHostedDomainAllowed(hostedDomain: string | undefined): boolean {
    if (this.allowedDomains.length === 0) return true;
    const domain = normalizeDomain(hostedDomain);
    if (!domain) return false;
    return this.allowedDomains.includes(domain);
  }

  private extractBearerToken(request: Request): string | null {
    const authHeader = request.headers.get('Authorization');

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Use a genuine Google-issued ID token from a real OAuth code exchange.
  2. In tests, include sub in the mock token payload when minting tokens.
  3. Ensure your issuer configuration points only at Google's issuer URLs so foreign tokens can't reach this code path.
  4. Log the decoded payload (without secrets) to confirm which claims the offending token actually carries.

Example fix

// before (test mock without subject)
const payload = { aud: clientId, iss: 'https://accounts.google.com' };

// after
const payload = { aud: clientId, iss: 'https://accounts.google.com', sub: 'user-123', exp: ... };
Defensive patterns

Strategy: validation

Validate before calling

const { payload } = decodeJwt(token);
if (typeof payload.sub !== 'string' || payload.sub.length === 0) {
  throw new Error('Token has no subject claim; cannot identify user');
}

Type guard

function hasSubject(p: object): p is { sub: string } {
  return 'sub' in p && typeof (p as { sub?: unknown }).sub === 'string' && (p as { sub: string }).sub.length > 0;
}

Try / catch

try {
  const user = await provider.verifyIdToken(token, nonce);
} catch (err) {
  if (err instanceof Error && err.message === 'Google ID token is missing subject') {
    // token is not a valid identity assertion: reject and restart flow
    return res.redirect('/login');
  }
  throw err;
}

Prevention

When it happens

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

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

Related errors


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