Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allow users access only to their own data in Firebase database?

I'm trying to have a data structure like this and to ensure that a user can only pull in their own data, since all the processing is done client side.

What database security rules would I have to use so that User1 can access their own posts, but cannot access User2's posts?

(I'm using Firebase web)

Sample database structure:

{
  "posts" : {
    "001" : {
      "text" : "note 1",
      "userID" : "User1"
    },
    "002" : {
      "text" : "note 2",
      "userID" : "User1"
    },
    "003" : {
      "text" : "note 3",
      "userID" : "User2"
    }
  }
}

Sample database query:

firebase.database().ref('/posts/').once('value').then(function(snapshot) {
  console.log(snapshot.val()); // Returns all 3 posts
});
like image 521
Slbox Avatar asked Mar 10 '23 00:03

Slbox


1 Answers

In your current structure it is very easy to secure data access to each post's creator:

{
  "rules": {
    "posts": {
      "$postid": {
        ".read": "data.child('userID').val() === auth.uid"
      }
    }
  }
}

This is all that is needed: now each user can only read their own posts.

But there is one big problem with this approach: no-one can now read from /posts, so no-one can get a list of all posts.

And to grant someone the ability to list posts, you must give them read access to /posts. And since you cannot revoke a permission on a lower level, that means that you at that point they can read all posts, not just the ones they created.

This is known within Firebase as rules are not filters: you cannot use rules to filter data. We've covered it quite a bit here on Stack Overflow, so I recommend you also check out some other questions on the topic.

There are quite a few solutions to the problem.

Secondary index

A common solution is to create a list of the post IDs that each user has access to. This is often called a (secondary) index and adds this additional data to your model:

{
  "userPosts" : {
    "User1": {
      "001" : true,
      "002" : true
    },
    "User2": {
      "003" : true
    }
  }
}

Now you secure access to the original posts as before, but then secure access to the secondary index with:

{
  "rules": {
    "userPosts": {
      "$userid": {
        ".read": "$userid === auth.uid"
      }
    }
  }
}

So each user can read the list of postIDs they have access to, and can then read each individual post under /posts/postID.

Store each user's posts under a separate node

In your case, there is a simpler solution. I'd model that data slightly more hierarchical, with each user's posts under their own UID:

{
  "posts" : {
    "User1": {
      "001" : {
        "text" : "note 1",
        "userID" : "User1"
      },
      "002" : {
        "text" : "note 2",
        "userID" : "User1"
      },
    },
    "User2": {
      "003" : {
        "text" : "note 3",
        "userID" : "User2"
      }
    }
  }
}

Now you can secure access with:

{
  "rules": {
    "posts": {
      "$userid": {
        ".read": "$userid === auth.uid"
      }
    }
  }
}

And each user can read and list their own posts. But do keep the secondary index in mind, since you're likely to need it sooner or later.

like image 86
Frank van Puffelen Avatar answered Apr 06 '23 10:04

Frank van Puffelen