yikart/AiToEarn · error · Error

Invalid Google token

Error message

Invalid Google token

What it means

googleLogin verifies a Google ID token with google-auth-library's OAuth2Client.verifyIdToken. After verification, ticket.getPayload() must return the decoded claims; if it returns null/undefined the token cannot be trusted and the service throws Error('Invalid Google token'). This guards against empty or malformed tokens reaching the login flow.

Solutions

  1. Check the client is sending the real Google ID token (credential/id_token), not an access token or empty string
  2. Verify the clientId passed as audience matches the OAuth client that issued the token
  3. Validate the token string is non-empty before calling googleLogin
  4. Upgrade google-auth-library to the latest version
  5. Catch this in the controller and return 401 instead of a raw 500

Example fix

// before
if (!googleUser) {
  throw new Error('Invalid Google token');
}
// after
if (!googleUser || !googleUser.sub || !googleUser.email) {
  throw new UnauthorizedException('Invalid or incomplete Google token payload');
}
Defensive patterns

Strategy: validation

Validate before calling

if (typeof idToken !== 'string' || idToken.split('.').length !== 3) {
  throw new UnauthorizedException('A valid Google id_token is required');
}

Type guard

function hasGooglePayload(t: unknown): t is { sub: string; email: string } {
  return !!t && typeof t === 'object' && 'sub' in t && typeof (t as any).sub === 'string';
}

Try / catch

try {
  await authService.googleLogin(idToken, clientId);
} catch (err) {
  if (err.message.includes('Invalid Google token')) {
    return res.status(401).json({ error: 'Please sign in with Google again' });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling googleLogin with an idToken that is empty, malformed, or decodes to no payload — i.e. verifyIdToken resolves but getPayload() returns falsy.

Common situations: Frontend sends an empty string token when the user aborted Google Sign-In; wrong clientId/audience causing an unexpected payload shape; passing an access_token instead of an id_token; an outdated google-auth-library version behaving differently.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/30e8089ee815735a. Report an issue: GitHub.

Appendix: source

Thrown at project/aitoearn-electron/server/src/modules/plat/google/google.service.ts:403

  /**
   * Google登录
   * @param clientId Google客户端ID
   * @param credential Google认证凭证
   * @returns Account
   */
  async googleLogin(clientId: string, credential: string): Promise<any> {
    try {
      console.log('Verifying Google token with:');
      // 验证Google token
      const ticket = await this.oauth2Client.verifyIdToken({
        idToken: credential,
        audience: clientId,
      });
      console.log('ticket',ticket)
      const googleUser = ticket.getPayload();
      console.log('payload',googleUser)
      if (!googleUser) {
        throw new Error('Invalid Google token');
      }

      console.log('Google login success, googleUser:', googleUser);

      const googleAccount = {
        googleId: googleUser.sub,
        email: googleUser.email,
        // accessToken: result.data.access_token,
        refreshToken: null,
        // expiresAt: result.data.expires_in
      };

      let userInfo: User | null = null;
      // 优先用 Google ID 查找(最准确)
      if (googleUser.sub) {
        userInfo = await this.userModel.findOne({
          'googleAccount.googleId': googleUser.sub,
          status: UserStatus.OPEN,

View on GitHub (pinned to d3aa8bea5b)