Allan Tokuda
Allan Tokuda

Reputation: 369

Firebase: how to require auth to list all nodes, yet allow anonymous read/write to individual nodes?

I'm writing an invitation application, and would like to email individual people unique URLs, e.g.

http://www.example.com/invitation.html?inviteID=-Jkbw6ycU7ZUOipmqlb5

The HTML app contains JavaScript that connects to a particular Firebase, looking up a node by the inviteID from the URL. Example:

https://[email protected]/-Jkbw6ycU7ZUOipmqlb5

Each top-level node looks roughly like

-Jkbw6ycU7ZUOipmqlb5: {
  email: '[email protected]',
  people: [
    {name: 'Joe', accept: true},
    {name: 'Jane', accept: false}
  ],
  comments: 'Jane can't make it, but I'm looking forward to it!'
}

This already works great! But I'm having trouble understanding how to properly secure the data. I need the recipients to continue to be able to access those URLs without authentication - anyone who supplies a node ID can read and write to that node and its children - and yet I need to require auth to see the Firebase at its top level, so that invitees cannot see (or modify!) anyone else's responses without knowing other inviteIDs. How can I do this?

{
  "rules": {
    ".read": ??
    ".write": ??
  }
}

I expect both .read and .write will need a rule that means something like this:

"You requested a specific child node, not the top level node; otherwise you must be an authorized user (auth != null) to see the top level node."

The app is written in ReactJS and communicates with Firebase roughly like this:

componentWillMount: function() {
  var dbAddress = '[email protected]/';
  this.firebaseRef = new Firebase(dbAddress + this.props.inviteId);

  this.firebaseRef.on("value", function(dataSnapshot) {
    this.setState(dataSnapshot.val());
  }.bind(this));
},

onSend: function() {
  this.firebaseRef.set(this.state);
},

Upvotes: 1

Views: 114

Answers (1)

CPP
CPP

Reputation: 1051

I have been reading the various firebase docs trying to find a similar solution.

Assuming your firebase json structure is something like the following:

{ Invitations: {
    -Jkbw6ycU7ZUOipmqlb5: {
        email: '[email protected]',
        people: [
            {name: 'Joe', accept: true},
            {name: 'Jane', accept: false}
        ],
        comments: 'Jane can't make it, but I'm looking forward to it!'
    }
    -Jkbw6ycU7ZUOipmqlb6: {
       ... another invitation ...
    }
    -Jkbw6ycU7ZUOipmqlb7: {
       ... another invitation ...
    }
}

I came up with the following security config which appears to do what you require:

{
    "rules": {
        ".read": false,
        ".write": false,
            "invitations": {
            "$inviteid": {
                ".read": true,
                ".write": true
            }
        }
    }
}

Actually the top level read/write false may be inferred because if I set the config as the following it seems to work in the same way:

{
    "rules": {
            "invitations": {
            "$inviteid": {
                ".read": true,
                ".write": true
            }
        }
    }
}

Now I cant seem to be able to browse the invitations as in if I try and mount at the following points I get permission denied (assuming your firebase address is https://[email protected]/:

this.firebaseRef = new Firebase('https://[email protected]/');

this.firebaseRef = new Firebase('https://[email protected]/invitations');

where as mounting at the following level lets me in:

this.firebaseRef = new Firebase('https://[email protected]/invitations/-Jkbw6ycU7ZUOipmqlb5');

Not sure if what I have done is actually achieving your requirements from a security perspective (i.e. is it actually secure?).

Would appreciate any feedback from the expert firebase community on this approach.

Upvotes: 1

Related Questions