Search code examples
firebasegoogle-cloud-firestorefirebase-security

Firestore Security Rule: How to validate Incoming Read Requests with Subcollection Field Values


I have a Firestore collection named 'users' that stores user information. Within this collection, there is a subcollection named 'shops,' containing documents listing the shops each user is permitted to access. The 'shops' subcollection includes a field named 'shopId,' which serves as a unique identifier for each shop.

Each incoming read request contains a 'shopId.' I am looking to create a Firestore security rule that validates each incoming request. Specifically, the rule should verify whether the 'shopId' in the incoming request matches the 'shopId' in the 'shops' subcollection of the user document associated with the request, then grant read permission to document from any collection with shopId matching the client request.

Here is my code, but are not working.

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow read access to user documents if conditions are met
    match /users/{userId} {
      allow read: if request.auth != null &&
        // Check if the user document exists and if the 'shopId' in the request matches the 'shopId' in the user document
        exists(/databases/$(database)/documents/users/$(request.auth.uid)/shops/$(request.data.shopId)) &&
        request.data.shopId == resource.data.shopId;
      
      // Allow reading all documents under any collection for the specified shopId
      match /{collectionId}/{document=**} {
        // Check if the user document exists and if the 'shopId' in the request matches the 'shopId' in the user document
        allow read: if request.auth != null &&
          exists(/databases/$(database)/documents/users/$(request.auth.uid)/shops/$(request.data.shopId)) &&
          request.data.shopId == resource.data.shopId;
      }
    }
  }
}

Solution

  • Your Firestore security rules seem to have a logical error. To achieve the desired behavior, you need to ensure that the 'shopId' in the request matches at least one 'shopId' in the 'shops' subcollection of the user document associated with the request. Here's an updated version of your rules:

    / Allow read access to user documents if conditions are met
    match /users/{userId} {
      allow read: if request.auth != null &&
        // Check if the user document exists and if the 'shopId' in the request matches any 'shopId' in the user document's 'shops' subcollection
        exists(/databases/$(database)/documents/users/$(request.auth.uid)/shops/$(request.data.shopId));
      
      // Allow reading all documents under any collection for the specified shopId
      match /{collectionId}/{document=**} {
        // Check if the user document exists and if the 'shopId' in the request matches any 'shopId' in the user document's 'shops' subcollection
        allow read: if request.auth != null &&
          exists(/databases/$(database)/documents/users/$(request.auth.uid)/shops/$(request.data.shopId));
      }
    }
    

    } }