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;
earlymorning logo

© 2025 - All rights reserved

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;