Firebase signInWithCustomToken 随机触发‘failed to fetch’错误且1小时内无法恢复的问题求助
Firebase signInWithCustomToken 随机触发‘failed to fetch’错误且1小时内无法恢复的问题求助
大家好,我这边遇到一个非常棘手的随机问题,折腾了好久都没找到原因,想请各位帮忙排查一下:
环境信息
- 操作系统:Mac OS X >=10.15.7、Windows >=10
- 浏览器:Chrome、Safari、Firefox
- 依赖版本:
- Firebase SDK ^12.1.0(仅使用Auth模块)
- Next.js 14 + TypeScript
- Clerk 客户端认证组件
- Firebase Auth 配置为内存持久化(为了避免IndexedDB相关问题)
问题详细描述
我们的业务逻辑是通过Clerk获取Firebase自定义token,调用signInWithCustomToken完成认证后访问Firestore。但有大概5%的用户会随机遇到以下问题:
- 调用
signInWithCustomToken后,函数会无响应挂起30秒,然后抛出failed to fetch错误 - 一旦触发这个错误,该用户在接下来的1小时左右时间里,完全无法通过这个函数完成认证——但同一个token在其他设备/浏览器上却能正常登录
我们的认证流程是严谨的:
- 先确认Clerk会话有效,获取到合法的Firebase自定义token(已经和Clerk团队确认过token本身是有效的)
- 等待Firebase Auth的
authStateReady()完成(用了内存持久化) - 确保所有前置条件(
isLoaded/isSignedIn/user都为true,无竞态条件)满足后,才调用signInWithCustomToken
已经排除的可能性
我们逐一排查了所有可能的环节,确认以下部分完全正常:
1. Firebase Auth初始化无问题
getBrowserAuth()能稳定返回Auth实例auth.authStateReady()能正常完成,无超时- Auth状态能正常读取,不存在未初始化的情况
2. Token生成与验证完全合法
- 调用
getToken({ template: 'integration_firebase' })始终返回非空token - Token验证通过,无空值或格式错误
- Clerk会话处于活跃有效状态,Clerk团队已确认token本身完全合法
- 同一个token在其他设备/浏览器上能100%正常完成登录
3. 所有前置条件都已满足
isLoaded && isSignedIn && user均为trueisFirebaseAuthReady为true(Auth监听器已成功建立)- 通过
!isAuthenticating状态严格避免了竞态条件
4. 错误触发的奇怪规律
- 错误只会在调用
signInWithCustomToken时稳定触发,其他环节无异常 - 触发错误时,Firebase Auth实例存在且已初始化,token也完全有效
- 最诡异的是:触发错误时没有任何网络请求发出——哪怕我们已经把持久化模式设为内存模式,按道理应该直接发起请求才对
相关代码片段
核心认证逻辑
import { useAuth } from '@clerk/nextjs'; import { auth } from '@/lib/firebase'; import { signInWithCustomToken } from 'firebase/auth'; const performAuth = async () => { const { getToken, isLoaded, isSignedIn, userId } = useAuth(); // 等待Clerk加载完成 if (!isLoaded || !isSignedIn || !userId) { return false; } try { // 从Clerk获取Firebase自定义token const token = await getToken({ template: 'integration_firebase' }); if (!token) { throw new Error('Token unavailable'); } // 就是这行会挂起30秒后抛出failed to fetch错误 const credentials = await signInWithCustomToken(auth, token); return !!credentials.user; } catch (err) { throw new Error('Authentication failed'); } };
Firebase Auth Provider 核心初始化代码
'use client'; import { createContext, useContext, useEffect, useState } from 'react'; import { useAuth, useUser } from '@clerk/nextjs'; import { onAuthStateChanged, getAuth, initializeAuth, inMemoryPersistence, type Auth } from 'firebase/auth'; import { app } from '@/lib/firebase'; import * as Sentry from '@sentry/nextjs'; let authSingleton: Auth | null = null; export function getBrowserAuth(): Auth { if (typeof window === 'undefined') { throw new Error('getBrowserAuth can only be called on the client side'); } if (!authSingleton) { try { // 用内存持久化初始化Auth authSingleton = initializeAuth(app, { persistence: inMemoryPersistence }); } catch { // 重复初始化时回退到getAuth authSingleton = getAuth(app); } } return authSingleton; } interface FirebaseAuthContextType { isFirebaseAuthenticated: boolean; firebaseAuthError: Error | null; isAuthenticating: boolean; isFirebaseAuthReady: boolean; firebaseAuth: Auth | null; } const FirebaseAuthContext = createContext<FirebaseAuthContextType | undefined>(undefined); const FIREBASE_AUTH_TIMEOUT = 3000; const FIREBASE_SETUP_TIMEOUT = 5000; export function FirebaseAuthProvider({ children }: { children: React.ReactNode }) { const { isSignedIn, user } = useUser(); const { getToken, isLoaded } = useAuth(); const [isFirebaseAuthenticated, setIsFirebaseAuthenticated] = useState(false); const [firebaseAuthError, setFirebaseAuthError] = useState<Error | null>(null); const [isAuthenticating, setIsAuthenticating] = useState(false); const [isFirebaseAuthReady, setIsFirebaseAuthReady] = useState(false); // 剩余的Provider监听与认证逻辑省略,核心初始化逻辑如上 // ... }
求助疑问
现在完全搞不懂为什么这个错误会随机出现,而且出现后会持续1小时左右。已经排除了token、初始化、网络环境这些常见问题,甚至连网络请求都没发出来就报错了。
想请教各位:有没有遇到过类似问题?或者Firebase Auth有没有什么我们没考虑到的机制(比如某种隐性限流?内存持久化下的隐藏状态问题?)可以指点一下排查方向?
内容来源于stack exchange
相关产品推荐
相关产品推荐

