Firestore Security rules
We define the security rules in the Firebase console or in a firestore.rules file. Firebase doesn't bill requests 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 then scope the rules to the current database. This is boilerplate code: we don't use the database wildcard variable.
match /databases/{database}/documents {
// ...
}
set rules for a given collection
We set rules for a given collection. The wildcard variable is the ID of the requested document. We may, for example, compare it with the user's authentication uid.
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:
if request.auth != null;
The user's authentication uid is available as request.auth.uid:
request.auth.uid
green-light specific documents
We may-green light the document if its ID matches the user's uid.
match /players/{player_id} {
allow read: if request.auth.uid == player_id;
}
Alternatively, we check if a field of the document matches the user's uid. For example, we check if the document's owner field matches the user uid. resource.data is the requested document.
match /planets/{planet_id} {
allow read: if request.auth.uid == resource.data.owner.id;
}
Note: if auth is null, trying to read uid triggers a failsafe mechanism which denies the request. The same failsafe triggers if we attempt to read a field that doesn't exist on the requested resource.
get authorization information in a separate document
We may read a different document with get()
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, to 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;
}
check requested document
resource.data.uid
resource.data.zone
resource.data.required_rank
data validation
The request's payload is exposed as request.resource. We may check if one of its field has the expected value.
request.resource.data.uid;
request.resource.data.age > 0
// A) user sends a post that mentions himself as uid
allow create : if request.auth.uid == request.resource.data.uid;
// B) user modifies a post that mentions himself as uid
// A) he must send a post that still mentions him as uid
allow update,delete: if
request.auth.uid == resource.data.uid
&&
request.auth.uid == request.resource.data.uid;