Search code examples
google-cloud-firestorefirebase-security

How to write Firebase overlapping rules?


I need to allow access to all documents only to authenticated users and prevent of writing invalid data.

For example I have a field that should contain only long type of data.

The problem is that if I have a rule that allow to write for authenticated user, then it ignores all other rules.

Here is what I have now:

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {
    match /Offers/{Offers} {
      allow write: if request.resource.data.val1 is float
      && request.resource.data.val2 is float;
    }
    match /{document=**} {
      allow read, write: if request.auth != null;
    }
  }
}

Solution

  • The problem comes from using =** in this rule:

    match /{document=**} {
      allow read, write: if request.auth != null;
    }
    

    This is saying that anyone who is authenticated has full read/write access to all documents in this collection, and in all collections under it. And once you've granted somebody this permission, you can't take it away at a lower level.

    The solution is to remove the =**:

    match /{document} {
      allow read, write: if request.auth != null;
    }
    

    Now authenticated users can still read/write all documents in this collection, but not in all collections under it. For the offers, you'll then want to add back that condition:

    match /Offers/{Offers} {
      allow write: if request.auth != null 
      && request.resource.data.val1 is float
      && request.resource.data.val2 is float;
    }
    

    So you'll end up repeating some of the conditions, which is normal. You'll often see these conditions then encapsulated in a function, so that it's easier to both reuse them, and to understand the meaning.

    For example, with two named functions, the rules could become:

    match /{document} {
      allow read, write: if isAuthenticated();
    }
    match /Offers/{Offers} {
      allow write: if isAuthenticated() && isValidOffer();
    }