Firebase Realtime Database Security Rules #1 - Getting Started

Firebase database rules are fast and clever.

A few things to know:

  1. They are super fast

  2. The permissions are path-based

  3. As soon as read or write permission is granted it is granted to any depth of the tree.

  4. As soon as read or write permission is granted it is granted to any depth of the tree.

  5. As soon as read or write permission is granted it is granted to any depth of the tree.

Concept 2 (3 and 4) are worth repeating because that’s the trick with Realtime Database Security Rules and is what makes the rules so fast to evaluate.  Once a permission is granted then operations can zoom down the tree.

Conversely, if for example write permission was granted at /abc/xyz and revoked at /abc/xyz/eee then rules evaluation would be very expensive, since every operation would have stop at every level of the tree and evaluate permission to keep going.

By default the rules are set to disallow reading and writing by anyone. Even you. :-)

{
  "rules": {
    ".read": false,
    ".write": false
}

These default rules are intended to block any abuse of your database data.

One common solution is to open up the rules with both .read and .write set to true. But setting .read and .write to true allows anyone to write to your database. And nobody really wants that.

Most apps require authentication of some kind and the start of the project is a good time to implement the authentication scheme. Your app needs to allow or block access to certain parts of the app so building that in from the beginning is the way to go.

Same goes for security of your data store. Build it in from the beginning.

The app implementation and security rules outlined here will use Google SignIn as the authentication scheme. Google SignIn is a great choice if you are writing a Firebase app! And Google smooths the implementation with great step-by-step doc. And once you have implemented Google SignIn the security rules can leverage the auth variable in our rules.

After integrating Google SignIn your auth token will be available when making requests to Firebase Realtime Database and you can tie the user’s auth UID to your rules.

After you log in with Google SignIn you can obtain the UID associated with your user ID with a log statement:

Log.d(TAG, "signIn UID: " + FirebaseAuth.getInstance().getCurrentUser().getUid()

You’ll use this UID to update your Realtime Database Security Rules.

So let’s return to the Realtime Database Security Rules. By using the built-in variable and field auth.uid we can create a rule that allows writing to the database for a user whose UID matches ours.

We’ll first check that auth.uid is not null in order to block anyone who is not authenticated. And if auth.uid is not null then we’ll compare the value to our UID.

Here is our new rule:

"rules": {
    ".read": "auth.uid != null && auth.uid == 1234567890123456789012345678",
    ".write": "auth.uid != null && auth.uid == 1234567890123456789012345678"  
}

Below is code that writes to the database that writes:

  1. To a location off the root named “MyUsers” 

  2. A model representing a user (userModel). Its a simply POJO with a few String fields.

 DatabaseReference dbRef1 = FirebaseDatabase.getInstance().getReference().child("MyUsers");
dbRef1.setValue(userModel).addOnSuccessListener(new OnSuccessListener<Void>() {
   @Override
   public void onSuccess(Void aVoid) {
       Log.i(TAG, "Write to database is successful!");
   }
}).addOnFailureListener(new OnFailureListener() {
   @Override
   public void onFailure(@NonNull Exception e) {
       Log.e(TAG, "Write to database failed.");
       Log.e(TAG, e.getMessage());
   }
});

And here is the model that userModel is based on:

public class UserModel {
    String firstName, lastName, userUID;
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getUserUID() {
        return userUID;
    }
    public void setUserUID(String userUID) {
        this.userUID = userUID;
    }
}

When the code is executed and the user is logged in then we get our success message:

I: Write to database is successful!

And when the user isn’t logged in we get the “Permission denied” error:

W: setValue at /MyUsers failed: DatabaseError: Permission denied   
E: Write to database failed.   
E: Firebase Database error: Permission denied

And if a different Google user logs in we get “Permission denied” as intended:

W: setValue at /MyUsers failed: DatabaseError: Permission denied      
E: Write to database failed.      
E: Firebase Database error: Permission denied

Great! We are able to write to the database when logged in as ourself and other users are locked out. We’re on our way!