8

I'm kind of stymied on my Firebase read/write rules. I wish I had a way to set break points as it's checking authentication (there isn't a way, is there?).

My question is fairly simple and I feel like I should be able to understand it better. I feel like most of stems from not understanding fully the rules. This information is for a simple way to view products online and the owner needs to be able to add products. So I want the world to be able to read and people who log in be able to write.

My data looks like:

app
 Users
    email: xxx@xxx.com
 product
   0
     name: pizza

And my rules:

 {
    "rules": {
      "product":{
        ".read": true,
        ".write":"root.child('Users').child(auth.uid).child('email').val() === 'testuser@test.com'"
      }
    }
}

and I don't have a great grasp of what this means. I really just want to say, if the user is logged in, allow write capabilities. I would have hoped I could so something like ".write": "auth" meaning that if auth exists (user is logged in) then let him write. But I'm clearly not fully understand how the rules work. I'm using simple email/password authentication.

How are these rules tested and how do I pass parameters to them?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Aarmora
  • 1,143
  • 1
  • 13
  • 26

2 Answers2

14

I'll try to explain your .write security rule, which is:

"root.child('Users').child(auth.uid).child('email').val() === 'testuser@test.com'"

This is indeed not the easiest rule to start with and the Firebase documentation admittedly dives into the deep rather quickly.

After I log on to my application with twitter, my code will get an authData variable that tells it:

 authData
     uid: "twitter:4511241"
     provider: "twitter"
     id: "4511241"

There might be a few more properties, but this should be the gist of it. The authData will typically contain a child object with properties that are specific to the provider, so in this case twitter and you can normally find some pretty interesting stuff there. Such as in the case of Twitter, the user's twitter name.

 authData
     uid: "twitter:4511241"
     provider: "twitter"
     id: "4511241"
     twitter:
         userName: "puf"
         email: "puf@twitter.com" // note: this is just an example email address,
                                  // it doesn't exist (as far as I know)

This object is made available to my code, it is not stored anywhere in Firebase by default.

In your security rules, you can check against the same structure. But only a minimal subset of the information is available here, just enough to identify the user. The Firebase documentation for the auth object names these properties:

provider | The authentication method used ("password", "anonymous", "facebook", "github", "google", or "twitter").

uid |A unique user id, guaranteed to be unique across all providers.

With these properties, we can already allow a specific user write access to our data. Like I said in my comment to your answer:

".write": "auth.uid = 'twitter:4511241'"

This will allow me to write data when I log on with my @puf twitter account. But unfortunately it doesn't allow me to check against more user-friendly properties, such as the email address.

A common practice is to create a top-level Users node in your Firebase and add all the information for all the users under that node, identified by their uid. So in my case above:

 Users
     twitter:4511241
         userName: "puf"
         displayName: "Frank van Puffelen"
         email: "puf@twitter.com"
     github:913631
         userName: "puf"
         displayName: "Frank van Puffelen"
         email: "puf@github.com"

I left the provider and id off here, since they don't provide much value for the example

When you know a user's uid, you can look up his additional information in this node. And that is exactly what the security rules you had before does:

"root.child('Users').child(auth.uid).child('email').val() === 'testuser@test.com'"

So this will look from the root of your Firebase, for a node called Users, then for the uid of the logged in user and under that his/her email address.

The cool thing about checking for a specific email address, is that it is not tied to the user's provider. So no matter whether the user is logged on with twitter, github, facebook or some other supported provider, as long as they used the same email address for each account, they will be able to write the data.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • This explanation was amazing. Thanks for the great input. So if would change my Users "object" in firebase to `Users -> id -> email: testuser@test.com` my original condition would work? – Aarmora Jan 23 '15 at 22:17
  • The email matching seems like a cool idea, but is there a security risk here? I.e., how is the /Users node in the db populated? If it's populated by the client through normal REST verbs and not by the firebase service, what's to stop me from intercepting the insert of my profile information to /Users and putting in any email address I like, e.g. joefoo@foefoo.com? If joefoo has an account with your service, it seems like now I can access his stuff in the firebase db. **EDIT** just saw @PavelGeorgiev's comment which says essentially the same thing. – smacke Jan 29 '17 at 18:09
0

Doing the below seemed to do the trick. If the auth variable exists, then allow write abilities. Due to me not fully understanding these rules, I have to wonder if this is running against the current user or if it's checking to see if any user is logged in.

{
    "rules": {
      "product":{
        ".read": true,
        ".write":"auth !== null"
      }
    }
}
Aarmora
  • 1,143
  • 1
  • 13
  • 26
  • Hi Aarmora, this will indeed allow everybody read access and any user who is logged in to write the product node. A simple example that allows a specific user to write would be `".write": "auth.uid = "twitter:4511241"`. That value is the uid that I have in one of my apps when I authenticated using twitter. Since those uids are not the easiest to come by, it is more common to look up e.g. the email address of the user in the `.write` rule, which is exactly what the example in your question does. – Frank van Puffelen Jan 23 '15 at 19:19
  • I'll write up an answer to see if I can explain that rule. – Frank van Puffelen Jan 23 '15 at 19:20