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,但认为它适用于前端,且未解决问题。
原因分析
- 服务器端Firebase Auth的会话特性:服务器端初始化的Firebase Auth实例不会自动持久化用户会话,
auth.currentUser仅在实例初始化时尝试获取一次用户,无法自动同步前端的登录状态。长时间后,即使前端仍处于登录状态,服务器端的currentUser也会因为没有凭证刷新而变为null。 - 未正确传递用户凭证:NextAuth的会话和Firebase Auth的服务器端实例是独立的,没有将NextAuth中的用户凭证传递给Firebase Auth来验证当前用户。
- 代码拼写错误:
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
相关产品推荐
相关产品推荐

