codef0rmer
codef0rmer

Reputation: 10520

Firebase security rule gives permission denied?

I'm struggling to set the proper security rules for my application.

An overview of the application I'm writing is that users can register themselves using email and password (I'm using Firebase Simple Login for this which works perfectly). Once logged in, user can add their todos.

angularFire('https://<firebase>/firebaseio.com/todos', $scope, 'todos');

And to add a new todo against any user, I simply update the todos model.

$scope.todos.push({
   user: '[email protected]',
   todo: 'What to do?'
});

This security rules I'm using to restrict non-registered user to add any todo:

  {
    "rules": {
      ".read": true,
      "todos": {
        ".write": "auth != null",
        ".validate": "auth.email == newData.child('user').val()"
      }
    }
  }

But it does not allow even an authenticated user to write any data and throwing an error, "FIREBASE WARNING: on() or once() for /todos failed: Error: permission_denied."

But If I add the following data in simulator then it works as expected.

{user: "[email protected]", todo: 'What to do?'} 

Here is the log:

/todos:.write: "auth != null"
    => true
/todos:.validate: "auth.email == newData.child('user').val()"
    => true
/todos:.validate: "auth.email == newData.child('user').val()"
    => true

Write was allowed.

Upvotes: 14

Views: 17922

Answers (1)

Anant
Anant

Reputation: 7428

push adds a new child with a randomly generated ID (in chronological order) to /todos. So, newData isn't pointing to what you think it is pointing to. Change your rules to:

{
  "rules": {
    ".read": true,
    "todos": {
      "$todoid": {
        ".write": "auth != null",
        ".validate": "auth.email == newData.child('user').val()"
      }
    }
  }
}

Update: Above rule is valid but angularFire currently writes the whole array back to the server causing the auth to fail. You can use angularFireCollection instead, to only write the new TODO back, like so:

$scope.todos = angularFireCollection(new Firebase(URL));

$scope.todos.add({user: '[email protected]', todo: 'What to do?'});

There's an open issue to optimize angularFire's behavior when new items are added to the list, but in the meantime you can use angularFireCollection to get the right behavior.

Upvotes: 17

Related Questions