I have chat documents like the following:
{
members: ['abc','def'] // 2 element list of member UIDs
// some other chat metadata
}
In firestore, I have the following rules on my collection:
function authed() {
return request.auth != null;
}
match /chats/{chatId} {
allow create: if authed()
&& request.resource.data.members is list
&& request.resource.data.members.size() == 2
&& request.auth.uid in request.resource.data.members;
allow read, update: if authed()
&& resource.data.members is list
&& request.auth.uid in resource.data.members;
allow list: if authed();
The last clause (allow list: if authed();) is necessary to allow users to run the following query on my client app:
const chatsRef = collection(firestoreDb, 'chats');
const LAST_MESSAGE_AT_PROPERTY: keyof Chat = 'lastMessageAt';
const MEMBER_PROPERTY: keyof Chat = 'members';
const q = query(
chatsRef,
where('members', 'array-contains', userId),
orderBy('lastMessageAt', 'desc'),
limit(20)
);
Without it, I would get a firebase error. However, this rule still permits any user to pull any other user's chats (I tested it by manually substituting another user's UID for userId).
From what I've read, on this last clause, there's no way I can inspect the actual query's where clause and ensure userId matches the request.auth.uid, and that the read rule that applies to each individual matched document should prevent the read of another user's chats, but this doesn't seem to be happening for me.
Could someone help tell me what I've got wrong in my rule? Here was the actual payload for this call:
{
"database": "projects/my-project/databases/(default)",
"addTarget": {
"query": {
"structuredQuery": {
"from": [{ "collectionId": "chats" }],
"where": {
"fieldFilter": {
"field": { "fieldPath": "members" },
"op": "ARRAY_CONTAINS",
"value": { "stringValue": "5Sdmw2sxJBdp0YnW3LtqgfYMrW83" }
}
},
"orderBy": [
{
"field": { "fieldPath": "lastMessageAt" },
"direction": "DESCENDING"
},
{ "field": { "fieldPath": "__name__" }, "direction": "DESCENDING" }
],
"limit": 20
},
"parent": "projects/my-project/databases/(default)/documents"
},
"targetId": 2
}
}
And this was the decoded bearer token it was sent with:
However, when I replace the above userId with another user's UID ('5Sdmw2sxJBdp0YnW3LtqgfYMrW83', who I'm not authenticated as), here's an example document returned which should not be returned as it should be a private conversation between two other users (NOTE: The screen shot shows memberUids instead of members, as I renamed the property, but assume that the value is called members in the photo to coincide with all the code snippets previously provided ):


MEMBERS_PROPERTY,LAST_MESSAGE_AT_PROPERTYanduserId. Can you reproduce with hard-coded values, or Firebase API calls for those? If so, please edit your question to use those. --- Same forchatsRef- please show how that is initialized.=