Firestore Security Rules – Protecting User Data

Question: How in the Sam Hill do I restrict access to data in Firestore to only the user who is requesting it?

Answer: Do this.

service cloud.firestore {
  match /databases/{database}/documents {
    match /collectionName1/{userId=**} {     
      allow create, update: if request.auth.uid == request.resource.data.uid;
      allow delete: if request.auth.uid == resource.data.uid
      allow read: if request.auth.uid != null;
    }

    match /collectionName2/{userId=**} {
      allow create, update: if request.auth.uid == request.resource.data.uid;
      allow delete: if request.auth.uid == resource.data.uid
      allow read: if request.auth.uid != null;
    }
  }
}
  • Attach the user id from Firebase Auth(this is what I use for authentication) as a property on the document data you are sending with every request.
  • For the delete rule, check the uid on the resource already stored
  • This is what is used in the rules to verify that the user authorized  is the same user sending items from the frontend.

 

  • A Non-authorized user will not be able to send documents without knowing the user id of the data they are trying to overwrite or update.
  • The read rule will protect against users not logged in with Firebase Auth, however, if a user wants to read items not belonging to them, they would need to know the user id of the other party to access that data (since the data is organized by user under their uid.)

After frustrating hours scouring Stack Overflow and reading the Firebase documentation getting the dreaded Missing or insufficient permissions error, I finally was able to only allow authorized users to access data belonging to them in the Firestore database for a project I’ve been working on.

I am using the built in Firebase Auth feature on the front end (you can find a great video tutorial by The Net Ninja here).

  • Firebase Auth sends the authenticated user id after they’ve logged in with every request to Firebase,
  • You can access the user id token on the request object in the rules using: request.auth.uid
  • Send the user uid from Firebase Auth on the front end with every resource sent to Firestore (in the case of updating, creating, or deleting a document).  You can access this property in the rules on: request.resource.data.[your uid prop here]
  • The read rule is needed in addition separately because you can’t match the auth uid on the request provided by Firebase Auth with any resource property, since no resource is sent with a GET read query.

NOTE: The context of these rules works if the database structure is:

/CollectionName/[UserIdStringMatchingLoggedInUser]/Documents

Just for completeness of example here is an example of a get and create/update query I am making from the front end:

// Attach user uid to the document data you are sending to Firebase:
const data = { myData: "stuff", uid: "[userUidFromFirebaseAuth]" }
export const getSavedData = (uid) => {
  return db.collection(`dataCollection/${uid}/usersData`)
     .get()
     .then(snapshot => {
        const data = snapshot.docs.map(snap => snap.data());
        return data;
    });
};
export const saveData = (data) => {
  return db.collection(`dataCollection/${data.uid}/usersData`)
      .doc(data.name)
      .set(data)
      .then(snapshot => {
          return "Data Saved!";
      })
      .catch(e => {
          console.error("Error saving data", e);
          return "Error saving data";
      });
};

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.