Firebase Realtime Database Security Rules #4 - Validate Data Values

At this point we have security rules based on the path to the data.  But how about security rules related to the values in the data?

Realtime Database Security Rules gotchu.

The .validate feature ensures that “the data being written conforms to a specific schema”.

In our example we have a first name, last name and (Google) user UID.  Let’s define the rules for what the model/object data can look like.

Using newData.hasChildren() we can add a .validate rule on the user object to enforce that our three fields are present in the object:

".validate": "newData.hasChildren(['firstName', 'lastName', 'userUID'])"

The new rules declaration containing our .validate rule looks like:

{
  "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",  
          ".validate": "newData.hasChildren(['firstName', 'lastName', 'userUID'])"
        }
    }
  }
}

Go to the simulator and now add values to the Data (JSON) section to test our .validate rule:

{
  "firstName": "Jane",
  "lastName": "Smith",
  "userUID" : "1234567890123456789012345678"
}

Run the test for set and update and be sure to use your Google UID in the path and userUID field.  You’ll see that our rule works!

Screen Shot 2019-07-04 at 11.08.25 AM.png

Publish the rules to save them at this point in time. 

Tip:  when using the Simulator to test rules the rules are in draft mode. You must always click Publish in order to save them!

Screen Shot 2019-07-05 at 4.37.56 PM.png

Now let’s go a little bit further and enforce some rules on the values of the fields . Use newData.isString() and newData.val().length to enforce that the value of firstName is a string and is also less than 8 characters in length:

"firstName" : {
  ".validate": "newData.isString() && newData.val().length <= 8"
}

Run the Simulator again. It passes!

Now is a good time to see what happens when the data passed does not satisfy the rule.  In the Data (JSON) field change the firstName value to “Elizabeth”, which is a name longer than the 8 characters allowed:

{
   "firstName": "Elizabeth",
   "lastName": "Smith",
   "userUID" : "1234567890123456789012345678"
}

Now run the simulation and see that it fails:

Screen Shot 2019-07-05 at 5.01.08 PM.png

The cool thing is that we can click on Details to find out which of our rules the data is failing:

Screen Shot 2019-07-05 at 5.07.02 PM.png

You can see clearly that the validation rule for the firstName field is failing. 

The Details view does not highlight which of the validation rules is failing, isString() or length of the value, but usually it will be obvious once you’ve drilled down to the exact line that is failing.  You can always remove portions of the && and run the simulator again in order to factor rules in or out.

Let’s add these validation rules:

  • lastName and userUID

  • allow 20 characters for lastName and firstName

  • validate that the length of the user UID is 28 characters

  • use a regular expression validation to confirm that the UID value contains only A-z and 0-9.

{
  "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",  
          ".validate": "newData.hasChildren(['firstName', 'lastName', 'userUID'])",
          "firstName" : {
              ".validate": "newData.isString() && newData.val().length <= 20"
          },
          "lastName" : {
              ".validate": "newData.isString() && newData.val().length <= 20"
          },
          "userUID" : {
              ".validate": "newData.isString() && newData.val().length === 28 && newData.val().matches(/^[A-z0-9]*/)"
          }
        }
    }
  }
}

Disclaimer:  These validation rules on the UID cannot be considered frozen. The format and characters allowed for a Firebase UID can change at any time and break your rule.  For now in this blog article we’ll use this rule just for demo purposes.

Notice that we use the === operator to confirm the length of the user UID.  The === operator confirms the variables are of the same type and value. 

From the Firebase documentation:

Screen Shot 2019-07-05 at 6.00.27 PM.png

So now we have some solid rules for our MyUsers branch!