You have unlimited access as a PRO member
You are receiving a free preview of 3 lessons
Your free preview as expired - please upgrade to PRO
Contents
Recent Posts
- Object Oriented Programming With TypeScript
- Angular Elements Advanced Techniques
- TypeScript - the Basics
- The Real State of JavaScript 2018
- Cloud Scheduler for Firebase Functions
- Testing Firestore Security Rules With the Emulator
- How to Use Git and Github
- Infinite Virtual Scroll With the Angular CDK
- Build a Group Chat With Firestore
- Async Await Pro Tips
Firestore Security Rules Guide
Episode 92 written by Jeff DelaneyIn this lesson, I will give you an in-depth guide to Firestore security rules. My goal is to make your rules code both maintainable and readable. We will go through a long list of common scenarios and write functions that will keep your code dry and readable.
Bookmark the Firestore Rules Reference.
How to Hack a Firebase App
Your Firebase app can hacked with ease if you fail to setup security rules. In the graphic below, I am simply sending a DELETE
request using cURL with an Firebase app’s database URL.
Your database URL is available in the frontend app, so a hacker can grab it from the Chrome network tab (or in the frontend source code). If backend rules are not present, anybody can send destructive requests to this endpoint and potentially wipe out all of your data.
Fortunately, backend rules in Firebase are relatively easy to configure. This lesson provides a collection of Firestore rules snippets and functions that you can drop into your app.
Security Rules Basics
Let’s quickly run through some of the most basic security rule concepts. You can define rules directly in the Firebase console or from your IDE Firebase Tools CLI. We are going to build out rules that look something like this:
Scope Rules to Specific Operations
Rules can be enforced on various read/write operations that occur in a clientside app. We can scope rules to each of the follow read operations.
allow read
- Applies to both lists and documents.allow get
- When reading a single document.allow list
- When querying a collection.
Write operations can be scoped as follows:
allow create
- When setting new data withdocRef.set()
orcollectionRef.add()
allow update
- When updating data withdocRef.update()
orset()
allow delete
- When deleting data withdocRef.delete()
allow write
- Applies rule to create, update, and delete.
Request vs Resource
Firestore gives us access to several special variables that can be used to compose rules.
request
contains incoming data (including auth and time)resource
existing data that is being requested
This part is confusing because a resource also exists on the request to represent the incoming data on write operations. I like to use use helper functions to make this code a bit more readable.
// service cloud.firestore { |
Functions are your best friend when writing Firestore rules. Extract any duplicated logic into a function, otherwise your rules will get messy quickly.
User Management Rules
The majority of security rules are centered around user authentication. We can get information about the current user via request.auth
.
Is the user signed In?
You will probably use this one frequently. It checks to see if the user authenticated.
// allow write: if isSignedIn(); |
Does the user own this document?
Certain data should only be accessed by the owner, such as their social security number, credit card details, private notes, an so on.
// match /accounts/{userId} { |
Notice how the function takes a userId
argument, which we can obtain from the wildcard path /accounts/{userId}
. This is the best way to validate ownership of data in Firestore.
Does this user have the correct access role?
Watch my comprehensive Role-Based User Auth with Firestore lesson for an end-to-end demo.
Validating a user role is actually very easy in Firestore. In the code below, I am assuming you have a user document that contains a roles
object that looks like { editor: true, admin: true }
.
This document will not be available on every request, but we can read it by using the get()
helper. We need to point to the user document, then interpolate the current user’s ID into the path with $(request.auth.uid)
. See the getUserData()
function below.
function getUserData() { |
Now we can write some functions to see if this document has an exact role, one of many roles, or all roles.
function userHasRole(role) { |
Data Validation Example
Now let’s combine some of the functions created earlier to build a robust validation rule. By chaining together rules with &&
we can validate the data structure of multiple fields as an AND
condition. We can also use ||
for OR conditions.
// allow update: if isValidProduct(); |
Time-based Rules Examples
Firestore also includes a duration
helper to generate dates that can be operated upon. For example, we might want to throttle updates to 1 minute intervals. We can create this rule by comparing the request.time
to a timestamp on the document + the throttle duration.
// allow update: if isThrottled() == false; |
Want to See More?
It’s impossible to cover every security rule scenario, but feel free to post your requirements in the comments below and I may add it to this article.