skip to Main Content

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")

2

Answers


  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.

    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search