0
votes

I wrote a simple chat application a while back to help me learn node and socket.io, I have recently been looking into firebase as a platform for an app/website, and I am running into an interesting problem I can't solve. Within the app there will be a chat app, there will be channels and all that good stuff, the hard part is there will be private chat, where 2 users can talk privately. The plan is to store the chat in a firebase database. Now I can easily restrict access to firebase databases based on if a user is authenticated, and even restrict user profile access to authenticated users and the user that owns that profile, but I am trying to figure out how to restrict access to the "private chat" children of the "chat" database, to only the 2 users that are in that conversation. I am thinking the database would look something like this...

{
  "chat": {
    "channels": ['topics', 'current', 'blah', 'blah', 'blah'],
    "{PRIVATE_CHAT_UID_GOES_HERE}": {
      "users": ["{USER_ID_1}", "{USER_ID_2}"],
      "messages": [{"from": "{USER_ID}", "message": "Hi there"},{...}]
      "createdOn": "DATE GOES HERE"
    },
    "{PRIVATE_CHAT_UID_GOES_HERE}": {
      "users": ["{USER_ID}", "{USER_ID}"],
      "messages": [{...}, {...}],
      "createdOn": "DATE GOES HERE"
    }
  }
}

Then I would restrict access to the child(private chat id) to only the users that are in the "users" array. That way no one can read or write to that particular chat, unless they are in that particular chat. I just have no idea how to go about it. I know you can do things like

".read": "auth !== null && auth.uid = $uid"

But I don't think that would be applicable since it limits usage to the owner of the uid, and the uid would be automatically generated when I add a child to "chat" to start a private chat between users.

Is something like this even possible, or is there some better way to structure the data that would allow an easy restriction of access to only the 2 users that are part of the conversation? I am trying to avoid having a node.js server sitting around just verifying if a user is in a chat, it seems like a pointless overhead, when you can list the database and handle auth directly from the database. I am more than happy to provide code samples of what I have, though I don't know that they are relevant. Thank you in advance for any help.

1

1 Answers

2
votes

Your first problem is that you're storing the users in an array. An array is an ordered collection that can have duplicate values. In your scenario you don't want duplicate values, and most likely the order doesn't matter. In such cases you should use a set data structure, which in Firebase is modeled as:

"users": {
  "{USER_ID_1}": true,
  "{USER_ID_2}": true
}

Now you can restrict read access to the chat room to its members by:

{
  "rules": {
    "chat": {
      "$roomid": {
        ".read": "data.child('members').child(auth.uid).exists()
      }
    }
  }
}

Be careful mixing different data types in the same node. It's often best to keep each entity in its own top-level node, relating the different types by their key. For example, I'd separate the room's messages, members, and other metadata:

chat
  rooms
    <roomid1>
      createdOn: ....
      .... other metadata for the room
  roomUsers
    <roomid1>
      user_id1: true
      user_id2: true
  roomMessages
    <roomid1>
      <message1>: { ... }
      <message2>: { ... }
      <message3>: { ... }