I'm using firebase in my Android project, I have a users collection in the Cloud Firestore database, users can only update some fields, but can read all, I know it is not possible to protect a subset of a document from being updated, and security rules can be applied only to the entire document, so I searched about this and the only solution I found is to make a sub-collection inside the user document and create a new document inside this sub-collection and finally put fields I want to protect in there, then I can easily secure this document applying this code in security rules section:
match /users/{userId}/securedData/{securedData} {
allow write: if false;
allow read: if true;
}
So now no one can write these fields, but anyone can read, and this is exactly what I want, but later I found that I need to query users based on fields inside the sub-collection, what is known as a collection group query which is not supported at the moment, so I ended up with two solutions:
- Retrieve all users and filter them at the client side, but this will increase the number of the document reads because I'm querying all users collection documents + an extra sub-collection document read for each user to get the needed field for filtering users.
Instead of making a sub-collection inside user document and incurring these problems, just keep all fields in the top level document(user document), allowing users to update any field.
match /users/{userId} { allow update, read: if true; allow create, remove: if false; }
But make an (onUpdate) cloud function to listen on user document update and detect changes in fields which aren't allowed to be modified by the user, so if the user tried to change these fields I can detect this and return modified fields to their previous values like this:
export const updateUser = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { const previousValue = change.before.data().securedField; const newValue = change.after.data().securedField; if (previousValue !== newValue) { return db.collection('users') .doc(context.params.userId) .update({ securedField: previousValue }); } });
Is the second solution secure? which is the best solution for this? or any other solutions?