firebasegoogle-cloud-firestorefirebase-security

Firestore Security Rules resource.data is not returning correctly


I have a collection named "dms" that has "firstUserId" and "secondUserId" as fields in its documents. The following rule is giving me permission denied error:

match /dms/{dmsId=**} {
  allow read, update, delete: 
    if request.auth.uid == resource.data.firstUserId ||
       request.auth.uid == resource.data.secondUserId;
  allow create: 
    if request.auth.uid == request.resource.data.firstUserId ||
       request.auth.uid == request.resource.data.secondUserId;
}

however, if I hardcode the value of firstUserId (e.g. request.auth.uid == "hard_coded_userId") it works.

So resource.data.whateverfieldname must not be returning the correct data. there are no typos in my field names and no extra whitespaces.

Flutter source code snippet:

return StreamBuilder(
  stream: FirebaseFirestore.instance
      .collection('dms')
      .doc(userMessages[index])
      .collection('messages')
      .orderBy(
        'createdAt',
        descending: true,
      )
      .snapshots(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const CircularProgressIndicator();
    }

    if (snapshot.hasError) {
      return Text(snapshot.error.toString());
    }

Solution

  • Your query can never work.

    1. You're reading the messages subcollection, but your rules depend on data that is only present in the dms document(s). The rules engine will not read that parent document, so resource.data.firstUserId and resource.data.secondUserId will never have any value.
    2. Even if it would, you always need to specify the same filter in your code as what your rules check on. The rules themselves never filter data

    So as Tom commented, if you want the rules to work as they are now, you'll have to duplicate the firstUserId and secondUserId values into each messages document too.

    Alternatively you might be able to get the parent document in rules specific to the subcollection and check those values. Note the might there, as I didn't check if this actually works.

    Also see: