0

I am trying to get all posts made between two users to each other in one firestore query, but I get permission denied. I have a "posts" collection with post documents that have the following fields:

{
    to : uid_of_user_receiving_post,
    from : uid_of_user_sending_post,
    queryIdentifier: uidsToQueryIdentifier(uid_of_user_receiving_post, uid_of_user_sending_post),
    ...
}

where:

uidsToQueryIdentifier = (uid1, uid2) => {
    if (uid1 < uid2) {
        return uid1 + "_" + uid2;
    }
    return uid2 + "_" + uid1;
}

Whenever I make a post document, I use uidsToQueryIdentifier() function as the value to the field queryIdentifier.

I am running this query on the client side with the following security rules and getting permission denied. The user object is authenticated and has a uid.


    match /posts/{postId} {
        allow read: if request.auth.uid == resource.data.to ||
                    request.auth.uid == resource.data.from;
        allow write: if true;
    }

      const queryIdentifier = uidsToQueryIdentifier(user.uid, friend.uid);

       let query = firestore().collection("posts")
            .where("queryIdentifier", "==", queryIdentifier)
            .orderBy("createdAt")
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • On Stack Overflow, please don't show pictures of text and code. Copy the text into the question itself and format it so that it's [easy to read, copy, and search](https://meta.stackoverflow.com/q/285551/807126). You can edit the question to correct this using the edit link at the bottom. – Doug Stevenson Feb 27 '23 at 14:08
  • As a sanity check, is `queryIdentifier` the expected value and does it match a document in the database? Additionally, your current rules do not [permit documents that do not exist](https://stackoverflow.com/a/67060730). – samthecodingman Feb 27 '23 at 14:16
  • @samthecodingman the queryIdentifier is the expected value it's the same when I query and the one on the document field. – Minh Luong Feb 27 '23 at 14:23
  • 1
    I suspect that Doug has hit the nail on the head, but as a debugging step, temporarily unlock your rules completely (e.g. `allow read: if true`), execute `query.get().then((qSnapshot) => console.log("results", qSnapshot.docs))`, then relock your rules and comment what happened here. – samthecodingman Feb 27 '23 at 14:44
  • [This thread on discovering members of chats](https://stackoverflow.com/a/66838632/3068190) could also be adapted to the `posts` collection that you are using here. Just add in a `members` array with both user IDs and use `where("members", "array-contains", user.uid)` along with `where("queryIdentifier", "==", queryIdentifier)`. – samthecodingman Feb 27 '23 at 14:47

2 Answers2

1

Firestore security rules are not filters (you should read and understand that documentation). What security rules do is place requirements on the queries that would be allowed by them. Your rule has requirements for the values of both to and from fields in the query, so your query must filter on the values of those fields using the UID of the user. Either one of these should work with the rule you have now:

       let query = firestore().collection("posts")
            .where("to", "==", uid)
       let query = firestore().collection("posts")
            .where("from", "==", uid)

Or, if you want to use queryIdentifier in your query, you would instead need to change your rule to place a requirement on that field instead.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
0

While Doug's answer explains why your current approach won't work, and his approach with two queries does work, my preferred solution to this problem is to add an array field to each document with the UID values of the participants in the post:

participants: [uid1, uid2]

With that in place, you can use a single query to get all posts the current user participates in:

firestore().collection("posts")
            .where("participants", "array-contains", "user.uid")
            .orderBy("createdAt")

If you ensure the participants values are always in the same order (similar to what you already do for queryIdentifier, you can even use an == on the field to replace you current condition:

firestore().collection("posts")
            .where("participants", "==", ["lowestUid", "highestUid"])
            .orderBy("createdAt")

This can work, but I usually prefer to have the string value like you have already as it's easier to troubleshoot.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807