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

Next.js集成Google One Tap登录报错:IdentityCredentialError求助

Next.js Google One Tap登录报错排查与解决

问题现象

在Next.js应用中实现Google One Tap登录时,弹窗可正常显示Gmail账号列表,但点击登录后弹窗提示:Can't continue with google.com. Something went wrong.

浏览器控制台出现以下错误:

  • The fetch of the id assertion endpoint resulted in a network error: ERR_FAILED
  • The provider's token fetch resulted in an error response code.
  • [GSI_LOGGER]: FedCM get() rejects with IdentityCredentialError: Error retrieving a token.

实现代码(GoogleOneTap.tsx)

/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";

import { useEffect, useCallback, useState } from "react";
import { signIn, useSession } from "next-auth/react";
import Script from "next/script";

declare global {
  interface Window {
    google: {
      accounts: {
        id: {
          initialize: (config: any) => void;
          prompt: (callback: (notification: any) => void) => void;
          cancel: () => void;
          revoke: (hint: string, callback: () => void) => void;
        };
      };
    };
  }
}

export default function GoogleOneTap() {
  const { data: session } = useSession();
  const [isGoogleScriptLoaded, setIsGoogleScriptLoaded] = useState(false);

  const handleCredentialResponse = useCallback((response: any) => {
    signIn("credentials", {
      credential: response.credential,
      redirect: false,
    }).catch((error) => {
      console.error("Error signing in:", error);
    });
  }, []);

  const initializeGoogleOneTap = useCallback(() => {
    if (window.google) {
      try {
        window.google.accounts.id.initialize({
          client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!,
          callback: handleCredentialResponse,
          context: "signin",
          ux_mode: "popup",
          auto_select: false,
        });
        window.google.accounts.id.prompt((notification: any) => {
          if (notification.isDismissedMoment()) {
            console.log(
              "One Tap was dismissed:",
              notification.getDismissedReason()
            );
          }
        });
      } catch (error) {
        if (
          error instanceof Error &&
          error.message.includes(
            "Only one navigator.credentials.get request may be outstanding at one time"
          )
        ) {
          console.log(
            "FedCM request already in progress. Waiting before retrying..."
          );
          setTimeout(initializeGoogleOneTap, 1000);
        } else {
          console.error("Error initializing Google One Tap:", error);
        }
      }
    }
  }, [handleCredentialResponse]);

  useEffect(() => {
    if (isGoogleScriptLoaded) {
      initializeGoogleOneTap();
    }
  }, [isGoogleScriptLoaded, initializeGoogleOneTap]);

  useEffect(() => {
    if (session) {
      window.google?.accounts.id.cancel();
    }
  }, [session]);

  return (
    <Script
      src="https://accounts.google.com/gsi/client"
      async
      defer
      onLoad={() => setIsGoogleScriptLoaded(true)}
      strategy="afterInteractive"
    />
  );
}

已排查项

  • Google开发者控制台已添加http://localhost和http://localhost:3000到授权JavaScript来源
  • Google Client ID配置正确
  • Chrome浏览器已启用第三方登录,无拦截器

解决方案

1. 完善NextAuth.js Credentials提供者配置

当前代码调用signIn("credentials"),但缺少对应的凭证验证逻辑。需要在[...nextauth].ts中配置Credentials提供者,验证Google返回的ID Token:

import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { OAuth2Client } from "google-auth-library";

const googleClient = new OAuth2Client(process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID);

export const authOptions = {
  providers: [
    Credentials({
      name: "Google One Tap",
      credentials: {
        credential: { type: "text" },
      },
      async authorize(credentials) {
        if (!credentials?.credential) return null;
        
        // 验证Google ID Token
        const ticket = await googleClient.verifyIdToken({
          idToken: credentials.credential,
          audience: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
        });

        const payload = ticket.getPayload();
        if (!payload) return null;

        // 返回用户信息给NextAuth
        return {
          id: payload.sub,
          name: payload.name,
          email: payload.email,
          image: payload.picture,
        };
      },
    }),
  ],
};

export default NextAuth(authOptions);

需先安装依赖:npm install google-auth-library

2. 禁用FedCM解决冲突

控制台错误提到FedCM(联邦身份验证服务),可在One Tap初始化时禁用该功能:

window.google.accounts.id.initialize({
  client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!,
  callback: handleCredentialResponse,
  context: "signin",
  ux_mode: "popup",
  auto_select: false,
  // 添加此行禁用FedCM
  use_fedcm_for_prompt: false,
});

3. 验证环境变量加载

在handleCredentialResponse中打印process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,确认客户端能正确读取到该值。

4. 避免重复初始化

添加状态标记是否已完成初始化,防止重复发起请求:

const [isInitialized, setIsInitialized] = useState(false);

const initializeGoogleOneTap = useCallback(() => {
  if (window.google && !isInitialized) {
    try {
      // 初始化代码...
      setIsInitialized(true);
    } catch (error) {
      // 错误处理...
    }
  }
}, [handleCredentialResponse, isInitialized]);

内容的提问来源于stack exchange,提问作者Ali Mazhar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:40:06