Firebase Security Rules Guide
Tuesday, January 21, 2025
Firestore security rules are essential for managing access to your database. They allow you to control who can read or write data while ensuring sensitive information remains protected. This guide provides practical steps and examples to help you create clear and secure rules for your Firestore projects.
Core Concepts
Setting Up Basic Rules
Lock Everything by Default
This ensures no data is accessible until explicitly allowed:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}
Limit Access to Specific Actions
Firestore allows you to target specific actions, such as reading or writing:
Example: Allow only read operations for a specific collection:
match /posts/{postId} {
allow read: if true;
allow write: if false;
}
Understanding Request and Resource Objects
Example: Use a function to check ownership:
function isOwner() {
return request.auth.uid == resource.data.ownerId;
}
User-Based Access Control
Allow Access for Authenticated Users
Only signed-in users can read or write:
match /posts/{postId} {
allow read, write: if request.auth != null;
}
Restrict Access to Document Owners
Grant access to a user based on their uid:
match /accounts/{userId} {
allow read, write: if request.auth.uid == userId;
}
Protect Data in Multi-User Scenarios
Ensure users can only modify their own data:
match /posts/{postId} {
allow create: if request.auth.uid == request.resource.data.uid;
allow update, delete: if request.auth.uid == resource.data.uid;
}
Prevent Anonymous Access
Block access for users signed in anonymously:
match /posts/{postId} {
allow create: if request.auth.token.firebase.sign_in_provider != 'anonymous';
}
Advanced Access Control
Allow Public Access with Exceptions
Make all data public except for specific collections:
match /{document=**} {
allow read, write: if false;
}
match /{collectionName}/{docId} {
allow read: if collectionName != 'private-collection';
}
Validate Data Before Writing
Ensure incoming data meets specific conditions:
function isValidEntry() {
return request.resource.data.amount > 10 &&
request.resource.data.category in ['js', 'ts', 'rust', 'python'];
}
match /products/{productId} {
allow create: if isValidEntry();
}
Time-Based Restrictions
Enforce time-based constraints to throttle updates:
function isThrottled() {
return request.time < resource.data.lastUpdate + duration.value(1, 'm');
}
match /posts/{postId} {
allow update: if !isThrottled();
}
Reusable Functions
Reusable functions make your rules more maintainable. For example:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
function isOwner(userId) {
return request.auth.uid == userId;
}
function hasVerifiedEmail() {
return request.auth.token.email_verified;
}
match /orders/{orderId} {
allow create: if isSignedIn() && hasVerifiedEmail() && isOwner(request.resource.data.userId);
allow read, update, delete: if isSignedIn() && isOwner(resource.data.userId);
}
}
}
Testing Your Rules
Conclusion
Effective security rules are the foundation of any Firestore project. By starting with strict access, validating data, and using reusable functions, you can create a secure and scalable database. Regularly test your rules to ensure they meet your app’s requirements.