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

© Antoine Weber 2026 - All rights reserved

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;