0

This is an implementation for a chat the fetches chatlist and realtime one on one chat.

The purpose of the provided code is to restrict the accessing of read and write based on the condition where the logged in user could only fetch the data corresponding to his uid provided during authentication.

rule:-

rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /messages/{document=**} {
      allow read: if request.auth != null && (request.auth.uid == resource.data.uid || request.auth.uid == resource.data.recipientId);
    
    }
     match /messages/{document} {
    allow write: if request.auth != null && (request.auth.uid == resource.data.uid || request.auth.uid == resource.data.recipientId);
    }

    // Chatlist Collection
    match /chatlist/{document=**} {
      allow read: if request.auth != null && (request.auth.uid == resource.data.uid || request.auth.uid == resource.data.recipientId);
      allow write: if request.auth != null && (request.auth.uid == resource.data.uid || request.auth.uid == resource.data.recipientId);
    }
    match /chatlist/{document} {
     allow write: if request.auth != null && (request.auth.uid == resource.data.uid || request.auth.uid == resource.data.recipientId);
    }
    }
  }

In the above code provided the rule has been written based on the above condition.

When trying to read or fetch the data from the collection chat list i am getting:

Uncaught Error in snapshot listener: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.

this the code to get the chatlist in realtime

 const fetchData = async () => {
  try {
    const messagesQuery = query(
      collection(db, "messages"),
      orderBy("createdAt", "desc")
    );
    const messagesSnapshot = await getDocs(messagesQuery);
    const messagesData = messagesSnapshot.docs.map((doc) => doc.data());
    console.log(messagesData,"messagesData");
    const chatlistQuery = query(collection(db, "chatlist"));
    const chatlistSnapshot = await getDocs(chatlistQuery);
    const chatlistData = chatlistSnapshot.docs.map((doc) => doc.data());
    console.log(chatlistData,"chatlistData");
    const filteredData = chatlistData.filter(
      (item) =>
        item.recipientId === currentUserUID || item.uid === currentUserUID
    );
    const uniqueUsers = new Map();

    filteredData.forEach((item) => {
      const existingItem = uniqueUsers.get(
        item.senderName || item.userName
      );
      if (!existingItem || item.createdAt > existingItem.createdAt) {
        if (item.recipientId === currentUserUID) {
          item.senderName = item.userName;
          item.recipientId = item.uid;
          item.email = item.currentUserEmail;
          uniqueUsers.set(item.userName, item);
        } else {
          uniqueUsers.set(item.senderName, item);
        }
      }
    });

    const updatedRecentChat = Array.from(uniqueUsers.values()).map(
      (item) => {
        const recentMessages = messagesData.filter(
          (message) =>
            (message.uid === currentUserUID &&
              message.recipientId === item.recipientId) ||
            (message.uid === item.recipientId &&
              message.recipientId === currentUserUID)
        );

        const sortedMessages = recentMessages.sort(
          (a, b) => b.createdAt - a.createdAt
        );

        if (sortedMessages.length > 0) {
          const recentMessage = sortedMessages[0];
          return {
            ...item,
            recentMessage: recentMessage.text,
            recentMessageCreatedAt: recentMessage.createdAt,
          };
        } else {
          // If there are no recent messages, set an empty string for the recentMessage field
          return {
            ...item,
            recentMessage: "",
            recentMessageCreatedAt: null,
          };
        }
      }
    );

    const sortedRecentChat = updatedRecentChat.sort((a, b) => {
      if (a.recentMessageCreatedAt && b.recentMessageCreatedAt) {
        return b.recentMessageCreatedAt - a.recentMessageCreatedAt;
      } else if (a.recentMessageCreatedAt) {
        return -1;
      } else if (b.recentMessageCreatedAt) {
        return 1;
      } else {
        return 0;
      }
    });
    setRecentchat(sortedRecentChat);
  } catch (error) {
    return error;
  }
};

And the same error occurs when individual chat is also made the code is provided below.

const sendMessage = async () => {
    const { uid, displayName, photoURL } = auth.currentUser;
    const newMessage = {
      text: input,
      name: displayName,
      avatar: photoURL,
      createdAt: serverTimestamp(),
      uid: uid,
      userName: userName,
      senderName: senderName,
      recipientId: recipientId,
    };
  
    try {
      await addDoc(collection(db, "messages"), newMessage);
      setInput("");
  
      const newChatListEntry = {
        uid: uid,
        recipientId: recipientId,
        userName: userName,
        senderName: senderName,
        email: email,
        currentUserEmail: currentUserEmail,
      };
  
      await addDoc(collection(db, "chatlist"), newChatListEntry);
      // Clear the message input field
  
    } catch (error) {
      console.error("Error adding document: ", error);
      // Handle the error as needed
    }
  };

Please let me know where i am getting this wrong?

danronmoon
  • 3,814
  • 5
  • 34
  • 56
am's
  • 21
  • 7

1 Answers1

0

Firebase security rules don't filter the data that is returned from the database by themselves. Instead the rules merely ensure that your code doesn't request any data it doesn't have access to.

Your code requests all documents from messages. Your security rules reject that, as they only a user to request their own messages.

To fix to, ensure that your code only requests the user's own messages, by adding the same filter/condition that your rules check for.

Also see:

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Really thanks for the help so i need to make changes where i am trying to access the collection in code level right? – am's Jun 09 '23 at 02:57
  • You need to make sure that your code matches the requirements of your security rules. So for `if request.auth != null && (request.auth.uid == resource.data.uid || request.auth.uid == resource.data.recipientId);`, you need to make sure in your code that there's a signed-in user, and that you have [conditions on your query](https://firebase.google.com/docs/firestore/query-data/queries) that match those in your rules. The links I provided in my answer should also give you good examples of how to do that. – Frank van Puffelen Jun 09 '23 at 03:56