Firestore Security rules
We define the security rules in the Firebase console or in a firestore.rules file. Firebase doesn't bill reads and writes denied by security rules.
rules version
rules_version = "2"
firestore scope
We start by scoping the rules to cloud.firestore
service cloud.firestore {
// ...
}
database scope
We scope the rules to the current database. This is boilerplate code: we don't use the database wildcard.
match /databases/{database}/documents {
// ...
}
set rules for a given collection
We target a collection. The document ID wildcard variable holds the requested document ID. We can, for example, compare the user document's ID with the authentication data.
match /users/{user_id}{
// ...
}
operations and condition
allow operation, operation: if condition;
operations
read
create
update
delete
authentication, user ID
If the user is not authenticated, request.auth is null. We may filter out unauthenticated users:
allow read: if request.auth != null;
The user's authentication uid (if logged-in) is available as request.auth.uid:
request.auth.uid
Note: if auth is null, trying to read uid triggers a failsafe mechanism that denies the request.
green-light specific documents
We green light the document if its ID matches a criteria:
match /players/{player_id} {
allow read: if request.auth.uid == player_id;
}
We green light the document if its field matches a criteria. resource.data represents the requested document. For example, we check the document's owner property against auth.uid.
match /planets/{planet_id} {
allow read: if request.auth.uid == resource.data.owner.id;
}
If the document is missing the field, the request is denied.
get authorization information in a separate document
We read a different document with get(). It is a billed read.
get(/databases/$(database) / documents / users / $(request.auth.uid)).data.rank
This unlocks a pattern where we read some authorization data in a different document, such as the user document, which would store the user's entitlements or ranks. This may not be a good architecture.
For example, to require a specific rank:
match /characters/{character_id} {
allow update: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.rank == "Game Master";
}
For example, we enforce that the requested character's zone is the same as the player's character's zone
match /overworld_characters/{overworld_character} {
allow read: if get(/databases/$(database)/documents/characters/$(request.auth.uid)).data.zone == resource.data.zone;
}
payload validation
request.resource.data is the request's payload. We validate critical fields such as the document's owner.
request.resource.data.age > 0
// A) user sends a post that mentions himself as uid. check the uid field.
allow create : if request.auth.uid == request.resource.data.uid;
// B) user modifies a post that mentions himself as uid
// A) check the uid field.
allow update,delete: if
request.auth.uid == resource.data.uid
&&
request.auth.uid == request.resource.data.uid;
Note: We can instead forbid writes coming from the client and perform validation in a Cloud Function with TypeScript.