Devdelly

Securely setting the owner of an object in firebase

September 29, 2019

So yesterday I played around with firebase and was amazed at how easy it is to use and how quickly you can get something running with the firebase realtime database. However once I needed to securely set the owner of an object, while sending the requests directly from the client side I faced a problem. I just couldn’t find anything in the documentation for my specific usecase. In the following I will present to you the problem and how I managed to solve it.

The goal

I wanted to build a commenting system where each user is able to submit comments. The requests had to be made directly from the client’s browser to firebase. Each requests must only be made by users that are authenticated and I needed to associate the comment with the user who created it.

A simplified version of the client call looked like the following code snipped:

firebaseDb.push({
  text: "the comment text",
  author: {
    id: firebaseAuthentication.guid,
  },
})

Since this code resided directly on the client side we needed a way to verify that the id that was sent to the datastore actually matched the id of the authenticated user.

What I found in the documentation

In general the documentation on the firebase security rules does a good job in explaining the concepts in an easy to understand and concise way. Therefore I was quickly able to change the security rules for the other parts of the system according to my needs. Verifying the id of the comment’s author, however, proved to be more challenging.

The most promising page regarding the combination of security rules and authentication is Security Rules and Firebase Authentication. Here we find an example of a similar case:

{
  "rules": {
    "users": {
      "$userId": {
        // grants write access to the owner of this user account
        // whose uid must exactly match the key ($userId)
        ".write": "$userId === auth.uid"
      }
    }
  }
}

This was a good starting point, however not exactly what I needed, because the comments in my system were not children of a user object, but actually belonged to a specific thread.

So instead of:

{
    "users": {
        "user1": {
            "comment1_1": { "text": "Hello" },
            "comment1_2": { "text": "bye world"}
        },
        "user2": {
            "comment2_1": { "text": "World"}
        }
        ...
    }
}

I needed a rule that verified the user id on a structure that made much more sense for my usecase:

{
    "thread1": {
        "comment1": {
            "text": "Hello",
            "author": {
                "id": "user1"
            }}
        },
        "comment2": {
            "text": "World",
            "author": {
                "id": "user2"
            }
        }
    }
}

The solution

In the end I found the solution to be:

  1. Only give write access for comments to logged in users.
  2. Use validate to check that the id on the newData object is the uid from the authentication
{
  "rules": {
      ".read": true,
      ".write": "auth.uid !== null",
      "$db_name": {
        "$entry_uid": {
          "author": {
            "id": {
              ".validate": "newData.val() === auth.uid"
            }
          }
        }
      }
  }
}

Summary

In this article we had a brief look into how we can validate that the correct owner is set on an object in firebase, even if the call is made from the client-side. I hope this will save you the time I had to spend to find a solution.


A blog by Manuel about everything related to software development