Firebase Realtime Database Security Rules #3 - data, newData and Simulator

At this point we have the start of some security rules for user profiles as well as a UserModel with first name, last name and user UID.

Starting with these rules we can make more use of the data in our User Model in order to lock down security even more.

{
  "rules": { 
      "MyUsers": {
          "$userUID": {
          ".read": "auth.uid != null && auth.uid == $userUID",
          ".write": "auth.uid != null && auth.uid == $userUID"            
        }
    }
  }
}

Using the newData variable we can access a representation of the new data object coming in, which means we can peek at the userUID field of UserModel and use it in our security rules. 

newData cannot be used for .read rules and will result in an error.  The reason is because read operations do not contain any data in the request so there is no newData object.

Let’s try to apply it to our write:

{
  "rules": { 
      "MyUsers": {
          "$userUID": {
          ".read": "auth.uid != null && auth.uid == $userUID",
          ".write": "auth.uid != null && auth.uid == $userUID && newData.child('userUID').val() == auth.uid",      
        }
    }
  }
}

A test of writing to the database is successful:

I: Write to database is successful!

But we need to lock down read access since no user should be able to read another user’s profile.  For that we can use the data object which is a representation of existing data in the database.

Here we check that the child named userUID in the existing data at that location in the database matches the current user UID:

{
  "rules": { 
      "MyUsers": {
          "$userUID": {
          ".read": "auth.uid != null && auth.uid == data.child('userUID').val() == auth.uid",
          ".write": "auth.uid != null && auth.uid == $userUID && newData.child('userUID').val() == auth.uid",      
        }
    }
  }
}

Great! We have rules!

So how can we test that users are blocked from reading and writing other users profiles?  For that we have the Firebase Realtime Database Security Rules Simulator!

Now is a good time to try the Simulator to test our new rules:

  1. Go to the Rules tab of Firebase Database console in your browser

  2. Choose Simulation Type = Set

  3. In Location paste the path that you are trying to write to. In our test we are writing to /MyUsers/1234567890123456789012345678

    • When debugging with the Simulator its a good idea to always paste the path that was output in your Permission Denied error so that you don’t make a mistake specifying the path to simulate

  4. You can skip specifying the Data (JSON) section for now because we haven’t yet set any rules regarding the schema of the data that can be written

  5. Enable Authenticated to be for a Google login and specify your Google UID to simulate your user login

  6. Click Run

It passed!

Now change the Location path to use another user’s GUID:

/MyUsers/1234567890123456789012345679

Click Run again.  This time you’ll get an error on the .write rule.

Try the simulation types of read and update to see that both will generate an error for another user.

So our rules are good!

Click Publish to save the rules!