You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google OAuth2报错:verifyIdToken方法需传入ID Token,求解决方案

Google OAuth2登录报错:The verifyIdToken method requires an ID Token排查与解决

问题背景

使用Google OAuth2的应用此前运行正常,现在登录功能突然失效,调用Google登录时后端返回错误:The verifyIdToken method requires an ID Token。排查后端google-auth-library代码未发现改动,现提供前后端代码寻求解决。


前端代码

index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import AuthProvider from "./context/AuthContext";
import { GoogleOAuthProvider } from "@react-oauth/google";

ReactDOM.render(
  <GoogleOAuthProvider clientId={process.env.REACT_APP_GOOGLE_CLIENT_ID}>
    <AuthProvider>
      <App />
    </AuthProvider>
  </GoogleOAuthProvider>,
  document.getElementById("root")
);

Login.js

import { useNavigate, useLocation } from "react-router-dom";
import { AuthContext } from "../context/AuthContext";
import { GoogleLogin } from "@react-oauth/google";
import axios from "../config/axiosConfig";

const isProduction = process.env.NODE_ENV === "production";
const authContext = useContext(AuthContext);
const navigate = useNavigate();
const { state } = useLocation();
const [message, setMessage] = useState(null);

function Login() {
  function handleSuccess(response) {
    axios
      .post("/api/auth/google", { token: response.tokenId })
      .then((res) => {
        const { user, isAuthenticated } = res.data;

        authContext.setUser(user);
        authContext.setIsAuthenticated(isAuthenticated);
        navigate(state?.path || "/diary");
      })
      .catch((err) => {
        console.log(err);
      });
  }

  function handleFailure(response) {
    setMessage("Authentication failed");
  }
  
  return (
    <GoogleLogin
      className="google-btn"
      redirectUri={
        isProduction
          ? process.env.REACT_APP_CLIENT_URL_PROD
          : process.env.REACT_APP_CLIENT_URL_DEV
      }
      onSuccess={handleSuccess}
      onError={handleFailure}
      theme="filled_blue"
      width="312px"
    />
  )
}

export default Login;

后端代码(auth.controller.js)

const { OAuth2Client } = require("google-auth-library");
const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);

const google = async (req, res) => {
  try {
    const { token } = req.body;

    const ticket = await client.verifyIdToken({
      idToken: token,
      audience: process.env.GOOGLE_CLIENT_ID,
    });

    const { given_name, family_name, email, sub } = ticket.getPayload();

    User.findOne({ email: email }, (err, user) => {
      if (!user) {
        const newUser = new User({
          first_name: given_name,
          last_name: family_name,
          email: email,
          googleId: sub,
        });

        newUser.save();

        generateTokens(newUser, res);

        res.status(201).json({
          isAuthenticated: true,
          user: given_name,
        });
      } else if (user && !user.googleId) {
        user.googleId = sub;
        user.save();

        generateTokens(user, res);

        res.status(200).json({
          isAuthenticated: true,
          user: user.first_name,
        });
      } else {
        console.log(err);
      }
    });
  } catch (error) {
    res.status(500).json({ success: false, message: error.message });
  }
};

错误原因

  1. Token字段名不匹配:你使用的@react-oauth/google库是新版OAuth库,其onSuccess回调返回的ID Token存储在response.credential字段,而非旧版的response.tokenId。前端传递给后端的token实际为undefined,导致后端verifyIdToken方法因缺少有效ID Token报错。
  2. 前端代码缺失导入:Login.js中未导入useContext和useState,会触发组件运行时错误,间接中断登录流程。
  3. 后端异步逻辑不规范:User.findOne使用回调函数与外层async/await不兼容,可能导致错误捕获不及时,但不是当前报错的直接原因。

解决办法

1. 修正前端Token获取逻辑

将handleSuccess中的response.tokenId改为response.credential:

function handleSuccess(response) {
  axios
    .post("/api/auth/google", { token: response.credential })
    .then((res) => {
      // 原有逻辑不变
    })
    .catch((err) => {
      console.log(err);
    });
}

2. 补全前端缺失的导入

在Login.js顶部添加:

import { useContext, useState } from "react";

3. 优化后端异步逻辑(可选,提升健壮性)

将User.findOne的回调改为async/await,避免嵌套和错误遗漏:

const google = async (req, res) => {
  try {
    const { token } = req.body;

    if (!token) {
      return res.status(400).json({ success: false, message: "ID Token缺失" });
    }

    const ticket = await client.verifyIdToken({
      idToken: token,
      audience: process.env.GOOGLE_CLIENT_ID,
    });

    const { given_name, family_name, email, sub } = ticket.getPayload();

    let user = await User.findOne({ email: email });
    if (!user) {
      user = new User({
        first_name: given_name,
        last_name: family_name,
        email: email,
        googleId: sub,
      });
      await user.save();
      generateTokens(user, res);
      return res.status(201).json({
        isAuthenticated: true,
        user: given_name,
      });
    }

    if (!user.googleId) {
      user.googleId = sub;
      await user.save();
      generateTokens(user, res);
      return res.status(200).json({
        isAuthenticated: true,
        user: user.first_name,
      });
    }

    generateTokens(user, res);
    return res.status(200).json({
      isAuthenticated: true,
      user: user.first_name,
    });

  } catch (error) {
    res.status(500).json({ success: false, message: error.message });
  }
};

4. 验证环境变量配置

确保前后端的Google Client ID完全一致,且环境变量在生产/开发环境中正确加载,避免因Client ID不匹配导致Token验证失败。


内容的提问来源于stack exchange,提问作者Alex Braga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 10:05:19