Firestore Security rules
ruleset
rules_version = "2"
firestore scope
service cloud.firestore {
// ...
}
database scope
match /databases/{database}/documents {
// ...
}
collection's documents scope
set the rule for each document of a collection
match /collection/{document_id}{
// ...
}
match /posts/{post_id}{
// ...
}
operations and condition
allow operation, operation: if condition;
operations
read
create
update
delete
authentication and user ID
If the user is authenticated with Firebase Auth, the request comes with its Firebase Auth user ID (auth.uid).
This user ID is the only piece of information we may reasonably trust.
request.auth.uid
filtering out unauthenticated users
An attacker may attempt to reach the database without authentication. If authentication is required, the attempt fails, and does not trigger billing.
if request.auth.uid != null;
document green-flagged for the user ID
a document may have a flag that greenlights the user ID. It may be:
the document's ID, which has been made equal to the matching user ID
match /users/{user_id} {
allow read: if request.auth.uid == user_id;
}
a document's field, such as owner or owner_id, which has been made equal to the matching user ID
match /diaries/{diary_id} {
allow read: if request.auth.uid == resource.data.owner_id;
}
document green-flagged for a user's rank, status, or permission flag
Firebase Auth may not store a user's rank or set of permission flags.
Instead, we store the flags in a firestore document that matches the auth ID, and make sure the user may not tamper them. We may store them in the users collection.
users/xyz
{
email
uid
rank: "Game Master"
}
fetch the permission, compare with requested document
fetch rank of requester
get(/databases/$(database) / documents / users / $(request.auth.uid)).data.rank
enforce specific rank
match /characters/{character_id} {
allow update: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.rank == "Game Master";
}
complete rule
service cloud.firestore {
match /databases/{database}/documents {
// Match each document in the 'characters' collection
match /characters/{characterId} {
allow update: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.rank == "Game Master";
}
}
}
enfore user's rank matching requested document rank
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
check payload, data validation
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;
check if the request user is an admin
allow write:
if get(/databases/$(database)/documents/users/$(request.auth.uid))
.data.admin == true;