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— isnullif not logged inrequest.auth.uid— the user's unique IDrequest.auth.token.email— the user's emailrequest.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.
