Search code examples
flutterfirebase-realtime-databasefirebase-security

I want to Firebase Realtime Database rules include auth.uid


This is my DB:

db

Each key was created by combining the user name. I want to make sure that only the members within each piece of data are accessible.

{
  "rules": {
    "chatroom": {
      ".write": "auth != null",
      "$room_id": {
        ".read": "$room_id.contains(auth.uid)"
      }
    }
  }
}

Since each key was created by combining the user name,

I thought I could use this .contains function to limit who can access it. In Flutter,

lastQuery = db.ref('chatroom').orderByChild('date');

data = await lastQuery.limitToLast(10).once();

But occurred Error:

[firebase_database/permission-denied] Client doesn't have permission to access the desired data.

With all allowed rules, this code works fine. For example,

{
  "rules": {
    ".read": "true",
    ".write": "true"
  }
}
{
  "rules": {
    "chatroom": {
      ".write": "auth != null",
      "$room_id": {
        ".read": "data.child('member').child(auth.uid).val() == true",
      }
    }
  }
}

Try this rules, Because key, value in member, it is made of UID : Bool.

This also causes an error.

What should I do?


Solution

  • Firebase's security rules don't filter data. Rather they merely ensure that the code is only trying to read data that it is allowed to access.

    That's why the code and rules always go hand-in-hand: the code must request exactly the nodes that you want it to be able to read, and then the rules must ensure that only those requests are allowed.


    So when you do this in your code:

    lastQuery = db.ref('chatroom').orderByChild('date');
    
    data = await lastQuery.limitToLast(10).once();
    

    The rules engine checks if the user has read permission on the chatroom node. And since there's no .read clause on that node or above it, the operation gets rejected.


    While you can define a condition for a query in your rules, those won't work here - as you can't mimic the contains operation from the rules in your actual code.

    The idiomatic approach is to create an additional top-level node where you track the chatroom IDs that each user has access to. So something like:

    userChatrooms: {
      "$uid1": {
        "$chatroomId1": true,
        "$chatroomId2": true
      },
      "$uid2": {
        "$chatroomId1": true,
        "$chatroomId3": true
      },
      "$uid3" {
        "$chatroomId2": true,
        "$chatroomId3": true
      }
    
    }
    

    With such a structure in place, you can:

    1. First read the chatroom IDs that the user has access to, based on their UID, from /userChatrooms/$uid. It's also easy to secure this structure with owner-only access.
    2. Then read each chat room's contents in turn from /chatroom/$chatroomID. Now your current rules work, since you no longer need to read the while root chatroom node, and you've already sufficiently secured access to the individual rooms with your current rules.

    Also see: Best way to manage Chat channels in Firebase