Firebase: set security rules depending on user roles

13,420

Based on the names of your user records, they don't match auth.uid, which is probably a Simple Login id, such as twitter:2544215.

Start by adjusting your users to be stored by their Simple Login uid:

+ myapp
  + users
    + twitter:2544215
      + email: "[email protected]"
      + roles
        + administrator: true
    + twitter:2544216
      + email: "[email protected]"
      + roles
        + moderator: true
    + ...
  + documents
    + -JVmo6wZM35ZQr0K9tJu
      + ...
    + -JVr56hVTZxlAI5AgUaS
      + ...
    + ...

Next, add a security rule so that administrators can access documents. You have a couple options here, depending on your specific use case.

  1. To give administrators write access to contents of each document:

    {
      "rules": {
        "documents": {
          "$documents": {
            ".write": "root.child('users').child(auth.uid).child('roles').child('administrator').val() === true"
          }
        }
      }
    }
    
  2. Or, alternatively, give them access to the whole collection:

    {
      "rules": {
        "documents": {
          ".write": "root.child('users').child(auth.uid).child('roles').child('administrator').val() === true"
        }
      }
    }
    

The difference between these two being the $documents variable that moves the security rule one step further into the hierarchy.

(This was mostly just an aggregation of comments by @Kato into answer form)

Share:
13,420
MarcoS
Author by

MarcoS

I'm a sailor. I love roaming in the Mediterranean sea on any wood piece you can stick a mast and a sail on. In my spare time I do software engineering in Turin, as a full-stack developer. I start my coding ages ago with x86 assembly; then I go with C, bash, Perl, Java; Android; currently I do MEAN stack: MongoDB, Express.js, Angular.js and of course Node.js. Obviously on the shoulders of the GNU/Linux O.S..

Updated on June 13, 2022

Comments

  • MarcoS
    MarcoS about 2 years

    I would like to implement "write" security rules in Firebase depending on users roles.
    My data structure is like this:

    + myapp
      + users
        + john
          + email: "[email protected]"
          + roles
            + administrator: true
        + mary
          + email: "[email protected]"
          + roles
            + moderator: true
        + ...
      + documents
        + -JVmo6wZM35ZQr0K9tJu
          + ...
        + -JVr56hVTZxlAI5AgUaS
          + ...
        + ...
    

    I want - for example - that only administrator users can write documents.
    These are the rules I've come to:

    {
      "rules": {
        ".read": true,
        "$documents": {
          ".write": "root.child('users').child(auth.uid).child('roles').child('administrator').val() === true"
        }
      }
    }
    

    But it doesn't work: not even administrator users can write documents...
    Is my understanding of Firebase security rules totally flawed?

    UPDATE: Just before Jenny's answer (believe it or not :-), I did implement the exact same solution he provides (of course based on Kato's comment).
    Though, making some tests, I could not let the rules structure

    {
      "rules": {
        "documents" {
          "$document" {
            ".read": "root.child('users').child(auth.uid).child('roles').child('documents').child('read').val() === true",
            ".write": "root.child('users').child(auth.uid).child('roles').child('documents').child('write').val() === true"
          }
        }
      }
    }
    

    work... I always got a warning like this:

    "FIREBASE WARNING: on() or once() for /documents failed: Error: permission_denied: Client doesn't have permission to access the desired data. "
    

    So I came up with this structure, instead:

    {
      "rules": {
        "documents" {
          ".read": "root.child('users').child(auth.uid).child('roles').child('documents').child('read').val() === true",
          ".write": "root.child('users').child(auth.uid).child('roles').child('documents').child('write').val() === true"
        }
      }
    }
    

    Which indeed works, for me: if I set a roles/customers/read node to true on a user he can read all documents, otherwise he can't (and the same for write).

    My doubts now are:

    • why I could not let the first rule (as suggested by Kato) work?
    • do you see any possible security hole in a rule like the one I did came up with?
    • are rules using "$" variables necessary/useful even if you don't have to allow/deny the readability/writeability of each single document based on it's key, but you just want to allow/deny the readability/writeability of a node as a whole?