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

Firebase中auth.currentUser有时为null导致密码更新失败求助

问题:tRPC服务器中Firebase Auth与NextAuth结合时,更新密码出现auth.currentUser为null的错误

在tRPC服务器中结合Firebase Authentication与NextAuth使用,用户登录一段时间后尝试更新密码,会因auth.currentUser为null而报错。相关代码如下:

const app = initializeApp(GoogleConfig)
const auth = getAuth(app)
const user = auth?.currentUser

const credential = EmailAuthProvider.credntial(req.input.email, req.input.currentPassword)
await reauthenticateWithCredential(user, credential).catch(err => console.log("Error", err))
await updatePassword(user!, req.input.newPassword).catch(err => console.log("err:", err))

return {
  message: "Password changed"
}

逻辑是先通过reauthenticateWithCredential重新认证用户,再调用updatePassword更新密码,但有时auth.currentUser会变为null,导致用户无法修改密码。曾尝试使用onAuthStateChanged,但认为它适用于前端,且未解决问题。


原因分析
  1. 服务器端Firebase Auth的会话特性:服务器端初始化的Firebase Auth实例不会自动持久化用户会话,auth.currentUser仅在实例初始化时尝试获取一次用户,无法自动同步前端的登录状态。长时间后,即使前端仍处于登录状态,服务器端的currentUser也会因为没有凭证刷新而变为null。
  2. 未正确传递用户凭证:NextAuth的会话和Firebase Auth的服务器端实例是独立的,没有将NextAuth中的用户凭证传递给Firebase Auth来验证当前用户。
  3. 代码拼写错误:EmailAuthProvider.credntial存在拼写错误,正确应为EmailAuthProvider.credential,这会导致凭证生成失败,进一步引发后续错误。

解决办法

1. 从NextAuth会话获取ID令牌,在服务器端验证并获取用户

在tRPC的路由中,先从NextAuth的会话里获取用户的Firebase ID令牌,然后用这个令牌在服务器端验证并获取用户实例,替代直接依赖auth.currentUser:

import { getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]"; // 引入你的NextAuth配置
import { signInWithCustomToken } from "firebase/auth";
import { TRPCError } from "@trpc/server";

export const changePassword = async ({ req, res }) => {
  // 获取NextAuth会话
  const session = await getServerSession(req, res, authOptions);
  if (!session?.user?.firebaseToken) {
    throw new TRPCError({ code: "UNAUTHORIZED", message: "用户未登录" });
  }

  const app = initializeApp(GoogleConfig);
  const auth = getAuth(app);

  try {
    // 使用ID令牌登录,获取服务器端的用户实例
    const userCredential = await signInWithCustomToken(auth, session.user.firebaseToken);
    const user = userCredential.user;

    // 生成正确的凭证
    const credential = EmailAuthProvider.credential(req.input.email, req.input.currentPassword);
    
    // 重新认证并更新密码
    await reauthenticateWithCredential(user, credential);
    await updatePassword(user, req.input.newPassword);

    return { message: "Password changed" };
  } catch (err) {
    throw new TRPCError({ code: "BAD_REQUEST", message: err.message });
  }
};

2. 配置NextAuth,在登录时获取Firebase ID令牌

在NextAuth的signIn和session回调中,获取并存储用户的Firebase ID令牌:

// [...nextauth].js
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";

export const authOptions = {
  providers: [
    // 你的认证提供者配置(比如CredentialsProvider)
  ],
  callbacks: {
    async signIn({ user, credentials }) {
      const auth = getAuth();
      // 以邮箱密码登录为例,获取Firebase用户凭证
      const userCredential = await signInWithEmailAndPassword(auth, credentials.email, credentials.password);
      const idToken = await userCredential.user.getIdToken();
      // 将令牌附加到用户对象
      user.firebaseToken = idToken;
      return true;
    },
    async session({ session, user }) {
      // 将令牌传入会话
      session.user.firebaseToken = user.firebaseToken;
      return session;
    },
  },
};

3. 完善错误处理,避免强制非空断言

移除代码中的user!强制非空断言,改为先判断用户是否存在再执行操作,避免空值引发崩溃。


内容的提问来源于stack exchange,提问作者Zillur Rahman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 03:05:02