Firestore Security Rules

Firestore Security Rules control who can read and write each document in your database. Without rules, either everyone can access everything (a serious security risk) or no one can access anything. Rules let you define exactly who gets access to what, down to the individual field level.

Rules as a Security Guard

Think of each Firestore document as a room in an office building. Security rules are the instructions given to the guard at the elevator. Before anyone reaches a room, the guard checks: Who are you? What do you want to do? Do the rules allow it?

Client request arrives
       |
       v
Firestore Security Rules evaluate the request:
- Who is making it? (request.auth)
- What document? (match path)
- What operation? (read / write)
       |
       v
Rules return: ALLOW or DENY
       |
       v
Allowed: Firestore executes the operation
Denied: Firestore returns "permission denied"

Rules Syntax

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // Match any document in the "posts" collection
    match /posts/{postId} {
      allow read: if true;                    // anyone can read
      allow create: if request.auth != null; // only logged-in users can create
      allow update, delete: if request.auth.uid
        == resource.data.authorId;           // only the author can edit/delete
    }

  }
}

Key Objects in Rules

request.auth

Contains information about the user making the request:

  • request.auth — is null if not logged in
  • request.auth.uid — the user's unique ID
  • request.auth.token.email — the user's email
  • request.auth.token.admin — a custom claim (if set)

resource.data

The current document's data (the version already stored in Firestore). Use it to check existing field values:

allow update: if resource.data.authorId == request.auth.uid;

request.resource.data

The data the client is trying to write (the new version). Use it to validate the incoming data:

allow create: if request.resource.data.title is string
  && request.resource.data.title.size() > 0
  && request.resource.data.title.size() < 200;

Common Rule Patterns

Only Authenticated Users

allow read, write: if request.auth != null;

Users Can Only Edit Their Own Data

match /users/{userId} {
  allow read: if request.auth != null;
  allow write: if request.auth.uid == userId;
}

Admin-Only Access

match /adminData/{docId} {
  allow read, write: if request.auth.token.admin == true;
}

Public Read, Authenticated Write

match /posts/{postId} {
  allow read: if true;
  allow write: if request.auth != null;
}

Validating Incoming Data

Rules can check that incoming data matches expected types and formats before allowing a write:

match /posts/{postId} {
  allow create: if request.auth != null
    && request.resource.data.keys().hasAll(["title", "body", "authorId"])
    && request.resource.data.title is string
    && request.resource.data.title.size() >= 1
    && request.resource.data.authorId == request.auth.uid;
}

Using Functions in Rules

Define reusable functions to keep rules readable:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    function isLoggedIn() {
      return request.auth != null;
    }

    function isOwner(userId) {
      return request.auth.uid == userId;
    }

    function isAdmin() {
      return request.auth.token.admin == true;
    }

    match /posts/{postId} {
      allow read: if true;
      allow create: if isLoggedIn();
      allow update, delete: if isOwner(resource.data.authorId) || isAdmin();
    }

    match /users/{userId} {
      allow read: if isLoggedIn();
      allow write: if isOwner(userId) || isAdmin();
    }
  }
}

Testing Rules in the Console

The Firebase Console has a Rules Playground under Firestore Database > Rules. Simulate operations by selecting a collection path, an operation (get, list, create, update, delete), and a simulated auth state. The playground shows whether your rules allow or deny the operation and points to the specific line causing the decision.

Deploying Rules

Rules live in a firestore.rules file in your project. Deploy them with:

firebase deploy --only firestore:rules

Always deploy rule changes to staging before production. A typo in a rule can lock everyone out of the database or open it entirely — test before pushing to live users.

Rules Do Not Apply to Admin SDK

The Firebase Admin SDK bypasses all security rules entirely. Code running with Admin SDK credentials — Cloud Functions, your own server — has full read and write access regardless of rules. This is intentional for trusted server-side operations. Never expose Admin SDK credentials in browser code or client applications.

Rate Limiting and Abuse Prevention

Security rules cannot rate-limit requests. A determined attacker can still send many requests, even if rules restrict what they read or write. For rate limiting, use App Check to verify requests come from your app, and place sensitive operations behind Cloud Functions that enforce their own limits.

Key Takeaway

Firestore Security Rules run on Firebase servers and evaluate every client request before it touches data. Use request.auth to check the caller's identity, resource.data to inspect existing document values, and request.resource.data to validate incoming data. Define reusable helper functions to keep rules readable. Test rules in the Playground before deploying. Remember that Admin SDK code bypasses all rules — keep Admin credentials strictly server-side.

Leave a Comment

Your email address will not be published. Required fields are marked *