Patterns for security with Firebase: per-user permissions for Cloud Firestore

Doug Stevenson
Firebase Developers
4 min readOct 17, 2019

If you allow users to sign into your app, I imagine you are storing some information about them — their information, preferences, status, and so on.

The user’s UID, as assigned by Firebase Authentication, is the key item of data that you’ll make use of in security rules in order to determine what that user can do with documents in Cloud Firestore when accessed directly from a web or mobile app.

Per-user Cloud Firestore documents, identified by the UID as the document ID

In my last post, you saw how I used a Cloud Functions auth trigger to create a per-user document using the Firebase Auth UID as the ID of the document in a collection. Per-user documents like this are very common. For example, if the user should be the only one able to write their own document in a collection called users, the code to write would look like this (in JavaScript), assuming that the user is authenticated and has a UID:

firestore.collection("users").doc(uid).set({
name: "Sparky"
})

In the screenshot here, the UID is the string starting with “ij”:

The rule to allow only that user to write their document looks like this:

match /users/{uid} {
allow write: if request.auth.uid == uid;
}

In this rule, the document ID in the users collection is wildcarded in the match as {uid} so that the rule applies to each user’s individual document. When the client code attempts to write the document, the rule will kick in, and the value of request.auth.uid contains their UID for easy comparison with the wildcard variable.

This pattern makes sense when there must be at most one document per user in a collection. It’s best to use the Firebase Authentication UID as a Cloud Firestore document ID in this case. You might be tempted to use an email address as a per-user document ID, but I don’t recommend that at all, as email addresses can change over time. UIDs will never change.

But what about situations where a user should own multiple documents in a collection? You might have a scheme to compose a new document ID using the concatenation of the UID and some other unique value, but that won’t work, because security rules are not capable of matching substrings of document IDs in wildcards. For example, this doesn’t work:

// Won't work - rules don't allow wildcards as substrings
match /messages/{uid}_{random} {
allow read, create, update: if request.auth.uid == uid;
}

So, we’ll need a different strategy.

User-owned Cloud Firestore documents, identified by the UID in a document field

Imagine you’re implementing a chat feature in your app. Let’s say there’s a collection called “messages” that holds all the messages in the app’s chat room. All authenticated users should be able to write messages in the room, so they will create documents using client code that looks like this:

firestore.collection("messages").add({
uid: uid,
text: "This is transmission from Earth."
})

Note that the document is being assigned a random ID (add() determines this automatically), and the user is providing their own UID as a field in the document, so everyone can know who wrote the message. A basic rule to give all authenticated users the permission to create and modify a document in the messages collection, and read them, looks like this:

match /messages/{id} {
allow read, create, update: if request.auth.uid != null;
}

This seems OK at first, until you realize that this rule allows any authenticated user to write a document containing a UID other than their own, or even an invalid UID! This effectively gives everyone the ability to “put words in the mouths of others”, assuming the other’s UID is known. This definitely needs to be prevented!

Let’s add a requirement that the incoming document’s uid property must be the same as the user’s UID who wrote it. Firebase security rules provide a way to examine the document being read or written by the client app, and that’s using the global request object. The document being written is available in request.resource, and the individual fields are in request.resource.data. Let’s make use of that object. Here’s a rule that ensures that the incoming document’s uid field is the same as the user’s UID, for both document creation and modification:

match /messages/{id} {
allow read: if request.auth.uid != null;
allow create:
if request.resource.data.uid == request.auth.uid;
allow update:
if resource.data.uid == request.auth.uid;
}

With this in place, it’s now an error for the user to create or modify a document using anything other than their own UID in the document. This makes it impossible for a user to fake a message from another user.

Protecting documents on a per-user basis using their UID is pretty straightforward. But I imagine you might also be wondering how to design security rules for group- or role-based access to documents. This is significantly more complicated, and I’ll cover that in the next post in this series, so stay tuned by following Firebase Developers here on Medium!

More blog posts about Firebase security rules

--

--