Search code examples
reactjsgoogle-cloud-firestorefirebase-security

Error in snapshot listener: FirebaseError: Missing or insufficient permissions


I have a Firestore database on Google Firebase with "village" collection inside of it. I want to limit read/write functionality for each document for specific user with their uuid as the document key.

I have added the rule to the "Rules" tab in Firestore, but I'm when I'm trying to fetch data I get an error saying that I don't have permisions to the Firestore...

Village collection Document/user uuid

This is my rule:

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

This is the code snippet which returns data successfully if I remove my rule from Firestore:

 useEffect(() => {
    const collectionRef = collection(db, "village");
    const q = query(collectionRef, orderBy("timestamp", "desc"));
    const unsubscribe = onSnapshot(q, (querySnapshot: any) => {
      setVillage(
        querySnapshot.docs.map((doc) => ({
          ...doc.data(),
          id: doc.id,
          timestamp: doc.data().timestamp?.toDate().getTime(),
        }))
      );
    });
    return unsubscribe;
  }, []);

This is the console.log enter image description here


Solution

  • Your current query is requesting all villages, and ordering them by their timestamp. Because your user can only access their own village, you need to change your query to only target what they have access to (remember that Firestore Security Rules are not filters).

    To update your code to work with the rules you defined, you need to switch from a query of all villages, to just their village.

    const currentUser = ...;
    
    useEffect(
      () => {
        if (!currentUser) return; // signed out
    
        const villageDocRef = doc(db, "village", auth.currentUser.uid);
    
        return onSnapshot(
          villageDocRef,
          {
            next: (docSnapshot) => { // don't use the any type
              if (!docSnapshot.exists()) { // don't forget to handle missing data
                return setVillage(null);
              }
              docData = docSnapshot.data();
              setVillage({
                ...docData,
                id: docSnapshot.id,
                timestamp: docData.timestamp?.toMillis() // shorter than .toDate().getTime()
              });
            },
            error: (err) => {
              // TODO: Handle Firestore errors
              console.error('Failed to get village data ', err);
              setVillage(null);
            }
          }
        );
      },
      [currentUser] // rerun when user changes
    );