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

如何修复React Hook中FirebaseError:权限缺失或不足问题

问题分析与修复

问题描述

结合React Hook、Next.js和Firebase Auth在Firestore中创建用户金币记录,预期新用户注册后自动创建包含uid、coins和createdAt字段的文档。但首次登录时出现权限错误:

hydration-error-info.js:27 Error in createUserCoinRecord: FirebaseError: Missing or insufficient permissions

退出后再次登录无此错误,且首次登录后Firestore未创建对应文档,二次登录才会创建。

错误原因

  1. 执行顺序不匹配:useCoinRecord中的useEffect监听NextAuth的session变化,一旦session存在就立即调用createUserCoinRecord。但此时Firebase Auth的自定义令牌登录流程(signInWithFirebase)还在异步执行中,Firestore请求时Firebase Auth实例处于未认证状态,导致request.auth为null,触发Firestore规则的权限校验失败。
  2. 二次登录缓存机制:第二次登录时,Firebase Auth会使用本地缓存的用户信息快速完成认证,此时session出现时Firebase已经处于登录状态,Firestore请求能通过权限校验,成功创建文档。

修复方案

方案1:修改useCoinRecord,监听Firebase Auth用户状态

将useCoinRecord的触发逻辑从依赖NextAuth session改为监听Firebase Auth的用户登录状态,确保只有在Firebase认证完成后才执行创建文档的操作:

import { useState, useEffect } from "react";
import { getAuth, onAuthStateChanged } from "firebase/auth";
import { doc, setDoc, getDoc, serverTimestamp } from "firebase/firestore";
import { db } from "../firebase/firebase";

export function useCoinRecord() {
  const [created, setCreated] = useState(false);
  const auth = getAuth();

  async function createUserCoinRecord(uid) {
    const userDocRef = doc(db, "users", uid);
    const userDocSnap = await getDoc(userDocRef);
    
    // 仅当文档不存在时创建
    if (!userDocSnap.exists()) {
      await setDoc(userDocRef, {
        coins: 100000,
        createdAt: serverTimestamp(),
      });
    }
  }

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, async (user) => {
      if (user) {
        try {
          await createUserCoinRecord(user.uid);
          setCreated(true);
        } catch (error) {
          console.error("Error in createUserCoinRecord:", error);
        }
      } else {
        setCreated(false);
      }
    });

    // 清理监听
    return () => unsubscribe();
  }, [auth]);

  return { created };
}

方案2:调整组件执行顺序,在Firebase登录成功后创建记录

如果希望保留原有的NextAuth session监听逻辑,可将创建文档的操作移到signInWithFirebase成功回调中,确保Firebase认证完成后再执行:

import { useSession } from "next-auth/react";
import { auth, db } from "../firebase/firebase";
import { doc, setDoc, getDoc, serverTimestamp } from "firebase/firestore";
// ...

const { data: session } = useSession();
const { coinBalance, setCoinBalance } = useCoinBalanceContext();
const [readyToFetchBalance, setReadyToFetchBalance] = useState(false);
const [created, setCreated] = useState(false);

async function createUserCoinRecord(uid) {
  const userDocRef = doc(db, "users", uid);
  const userDocSnap = await getDoc(userDocRef);
  
  if (!userDocSnap.exists()) {
    await setDoc(userDocRef, {
      coins: 100000,
      createdAt: serverTimestamp(),
    });
  }
}

useEffect(() => {
  if (session) {
    signInWithFirebase(session).then(async (uid) => {
      if (uid) {
        try {
          await createUserCoinRecord(uid);
          setCreated(true);
          setReadyToFetchBalance(true);
        } catch (error) {
          console.error("Error creating coin record:", error);
        }
      }
    });
  }
}, [session]);

// ... 其余代码不变

额外优化:确保Firestore规则与UID逻辑一致

当前规则中request.auth.uid == uid与你使用用户邮箱作为UID的逻辑匹配,无需修改,可补充创建权限的明确声明(可选):

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{uid} {
      allow read, write, create: if request.auth != null && request.auth.uid == uid;
    }
    match /users/{uid}/{document=**} {
      allow read, write: if request.auth != null && request.auth.uid == uid;
    }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 10:29:54