7
votes

Using firestore with angularfire2 rc 2.

All is working very nicely in development with no effective security rules.

These are the no security rules - where the client code will create, update and delete collections below the $COLLECTION_NAME/document without issue.

service cloud.firestore {
    match /databases/{database}/documents {
        match /collectionA/{userId=**} { 
            allow read, write: if true;
        }
        match /collectionB/{userId=**}  {
            allow read, write: if true;
        }
        match /collectionC/{userId=**}  {
            allow read, write: if true;
        }
        match /collectionD/{userId=**}  {
            allow read, write: if true;
        }
    }
}

Where I want to get to is to only allow users to access their own data.

I started out with the following rules:

service cloud.firestore {
    match /databases/{database}/documents {
        match /collectionA/{userId=**} { 
            allow read, write: if request.auth.uid == userId;
        }
        match /collectionB/{userId=**}  {
            allow read, write: if request.auth.uid == userId;
        }
        match /collectionC/{userId=**}  {
            allow read, write: if request.auth.uid == userId;
        }
        match /collectionD/{userId=**}  {
            allow read, write: if request.auth.uid == userId;
        }
    }
}

However, as soon as I add any sort of restrictive rule including even just validating the request is authorised.

match /databases/{database}/documents {
    match /collectionA/{userId=**} { 
       allow read, write: if request.auth;
    }
    match /collectionB/{userId=**}  {
        allow read, write: if request.auth;
    }
    match /collectionC/{userId=**}  {
        allow read, write: if request.auth;
    }
    match /collectionD/{userId=**}  {
         allow read, write: if request.auth;
    }
}

I get code permission errors.

[code=permission-denied]: Missing or insufficient permissions.

FirebaseError: Missing or insufficient permissions.

Each {userId} document contains a collection which in turn contains documents so a full path might be something like

const entryCollection: AngularFirestoreCollection<Entry> = this.angularfirestore.collection('collectionC/' + user.uid + '/records' + '/2017-40' + '/entries');

The requests should be authenticated as the access is only granted in the client following authentication using firebase authentication. Logging indicates the uid is present and indeed when creating the document below collectionA or B or C or D the user.uid document is named using the uid.

4

4 Answers

7
votes

The short answer is that {userId=**} results in userId being a path and not a string. This means that comparing it to request.auth.uid (which is a string) will fail. Instead, you'll likely want something like:

service cloud.firestore {
    match /databases/{database}/documents {
        match /collectionA/{userId}/{allSubcollections=**} { 
            allow read, write: if request.auth.uid == userId;
        }
    }
}

This will guarantee that userId is a string, and then match the appropriate subcollections (note that again, allSubcollections will be a path).

4
votes

In my case, I needed the permissions for creating the user as well so the other solutions did not work for me. I had to also allow access to /users/{userId}. Here is my code:

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, write: if request.auth.uid == userId;
      match /{allSubcollections=**} {
        allow read, write: if request.auth.uid == userId;
      }
    }
  }
}
2
votes

The following setup worked for me (I've used allChildren as opposed to allSubcollection):

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId}/{allChildren=**} {
      allow read, write: if request.auth.uid == userId;
    }
  }
}

allChildren will allow to read/write in any subcollections of a user document.

More information on this wildcard matching is here

1
votes

I resolved this issue like this.

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
       match /{allSubcollections=**} {
        allow read, write: if request.auth.uid == userId;
      }
    }
  }
}