Search code examples
flutterfirebasegoogle-cloud-firestorefirebase-security

Firebase Firestore Rules: Allow read/write only if auth.id == request.data.id


I am using these rules:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    
    // Documents can only be read or written by authenticated users
    match /{document=**} {
      allow read, write: if request.auth != null && request.auth.uid == resource.data.uid;
    }
  }
}

From my understanding, this should mean: "Allow read or write operations ONLY IF the user is authenticated and their uid matches the uid of the document they are reading or writing".

Yet, I continually get permission-denied errors.

This is the code I am using (Dart/Flutter):

  static FirebaseFirestore instance = FirebaseFirestore.instance;
  static CollectionReference colRef = instance.collection('someCollection');

With this wrapper method (updated):

Future<void> updateData() async {
    final uid_ = FirebaseAuth.instance.currentUser!.uid;
    final data = {'uid': uid_};
    await colRef.doc(uid_).set(data);
}

Basically, I am passing the user uid to the updateData function, and ensuring the document has the same uid so it conforms with the rules.

What am I doing wrong?

EDIT:

Things that I have tried:

  1. Ensured that the user was authenticated and that the correct uid was being passed to the wrapper function.

  2. Used the set method instead of the update method to avoid any issues if the document does not yet exist.

  3. Appended the uid to the data map after learning more about the resource object.

  4. Toggled the rules between request.auth.uid == resource.data.uid and request.auth.uid == resource.id.

  5. Changed the rule to only be request.auth != null, which ended up working as expected, but is a bit too broad for my comfort zone. Yet, this is proof that the user is authenticated. I just need a way to compare the auth.uid with either the document.id or uid within the data map object.

  6. Changed the wrapper method to explicitly use FirebaseAuth.instance.

The specific error:

flutter: [cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.


Solution

  • The key was that you're trying to write the uid field for the first time.

    Your rules now do this:

    resource.data.uid
    

    The resource here refers to the document as it exists before the current operation, which doesn't seem to have the uid value yet.

    If you want to refer to the document as it'll exist after the write operation (if that write operation is allowed), use:

    request.resource.data.uid
    

    Also see the documentation on the resource and request.resource variables.