Order of execution in Firestore query pipeline - why missing permissions?

My Firestore collection contains one document per user, and the user ID is the ID of the document.

I have the following rules:

    match /mycollection/{userId} {
      allow read, update, delete: if userId == request.auth.token.email;
      allow create, update: if request.auth.uid != null && userId == request.auth.token.email;
    }

This guarantees that

  • every user can read, update and delete "their" document,
  • a new user can create "their" document.

This works great when I load the user's document "by ID".

However, it does not work when the document is loaded via a query:

const query = db.collection("mycollection")
  .where("user", "==", userId)
  .where("writeDate", ">", writeDate)
  .get()

As you can see, the user only loads "their own document", so in my opinion, the request satisfies the access rules. However, I get a "Missing or insufficient permissions" error anyway.

Why?

I suppose this has to do with the execution order of queries and rules on Firestore, but I have not found anything in the documentation about this. As long as I only access the documents I have the right to read, everything should be fine, no?

In case you are wondering: I need the query to download the document only if it has changed (hence the "date" where clause). I know I can do that with realtime updates (using onSnapshot or similar). I do not want to know alternative approaches.


Solution 1:

Found it. The following rules do not work:

    match /mycollection/{userId} {
      allow read, update, delete: if userId == request.auth.token.email;
      allow create, update: ...
    }

These rules do work:

    match /mycollection/{userId} {
      allow read, update, delete: if resource.data.user == request.auth.token.email;
      allow create, update: ...
    }

The userId works only if the document is accesssed via its ID.

If the rule is based on a field of the document (here: user), it needs to use the field as resource.data.user.