Security Philosophy
The Firestore security rules are built on the following core principles:Default Deny
All operations are denied unless explicitly allowed by a rule
No Public Data
There are no globally readable collections; authentication is always required
User Listing Disabled
Listing the
/users collection is forbidden to prevent scrapingPath-Based Authorization
Ownership determined by URL path, avoiding costly database reads
Core Philosophy (from firestore.rules)
This ruleset enforces a strict user-ownership model where all user-generated content is stored within a private data tree associated with that user’s ID. Access to any document or subcollection is granted only if the requesting user is the authenticated owner of that data tree. This approach ensures strong data isolation and privacy by default.
Data Structure
All data is organized hierarchically under the top-level/users collection:
- A root document at
/users/{userId} - All personal data in subcollections beneath this root
- Complete isolation from other users’ data
Security Rules Implementation
The complete security rules are defined infirestore.rules:
Helper Functions
The rules use reusable helper functions to enforce security:isSignedIn()
isSignedIn()
Checks if the user is authenticated.Returns
true if a valid authentication token is present.isOwner(userId)
isOwner(userId)
Checks if the authenticated user’s UID matches the provided userId.This is the primary function for enforcing the ownership model.Example: A request to
/users/alice/argumentMaps/123 succeeds only if:- User is signed in (
request.auth != null) - User’s UID equals
"alice"(request.auth.uid == "alice")
isExistingOwner(userId)
isExistingOwner(userId)
Checks ownership on an existing document (used for safe updates/deletes).Prevents operations on non-existent documents. The
resource variable represents the existing document.Create Operation Helpers
Special validators ensure data integrity when creating documents:isCreatingOwnProfile(userId)
isCreatingOwnProfile(userId)
Validates that a new User profile has an Why? Ensures the document path and document content are consistent.Example: Creating
id field matching the document ID./users/alice requires the document to have { id: "alice", ... }isCreatingOwnSubcollectionDoc(userId)
isCreatingOwnSubcollectionDoc(userId)
Validates that subcollection documents have a Why? Denormalizes ownership into the document, making queries and data export easier.Example: Creating
userId field matching the owner./users/alice/argumentMaps/map1 requires:Update Operation Helpers
These validators prevent tampering with ownership fields:Collection Rules
User Profiles: /users/{userId}
Rule Breakdown
| Operation | Rule | Example |
|---|---|---|
| get | User can read own profile | Alice can read /users/alice |
| list | DENIED for all users | No one can list /users collection |
| create | User can create own profile | Alice can create /users/alice if id: "alice" |
| update | Owner can update, but id is immutable | Alice can update /users/alice profile |
| delete | Owner can delete own profile | Alice can delete /users/alice |
Why is list denied?
Why is list denied?
Privacy Protection: Listing the
/users collection would reveal all user IDs in the system.Attack Prevention: Prevents:- User enumeration attacks
- Data scraping
- Privacy violations
get using their known UID.Argument Maps: /users/{userId}/argumentMaps/{argumentMapId}
Rule Breakdown
| Operation | Rule | Example |
|---|---|---|
| get | Owner can read own maps | Alice can read /users/alice/argumentMaps/map1 |
| list | Owner can list own maps | Alice can list all /users/alice/argumentMaps |
| create | Owner can create with userId | Alice creates map with userId: "alice" |
| update | Owner can update, userId immutable | Alice can update map data |
| delete | Owner can delete own maps | Alice can delete /users/alice/argumentMaps/map1 |
Permission Scenarios
- Allowed Operations
- Denied Operations
Sources: /users/{userId}/sources/{sourceId}
- Private to each user
- Full CRUD operations for owner only
userIdfield required and immutable
Denormalization for Authorization
The security rules rely on denormalized data for efficient authorization:Why Store userId in Documents?
The security rules check both:
- Path structure:
/users/{userId}/... - Document field:
document.userId
- Fast validation: No
get()calls needed (which cost read operations) - Data integrity: Path and content always match
- Easy queries: Filter by
userIdwithout complex joins - Export-friendly: Documents are self-contained with ownership info
Example: Creating an Argument Map
Fromsrc/lib/actions.ts:
isOwner(userId)→user.uid == "alice"✅isCreatingOwnSubcollectionDoc(userId)→document.userId == "alice"✅
Performance Characteristics
Why Path-Based Authorization is Fast
No Database Reads in Security Rules
No Database Reads in Security Rules
Traditional authorization might look like:Our approach:Result: Every operation saves one Firestore read, reducing costs and latency.
Security Rule Evaluation Cost
| Approach | Cost per Operation | Latency |
|---|---|---|
| Path-based (our approach) | 0 reads | ~1ms |
Document-based (with get()) | 1 read | ~10-50ms |
Client-Side Error Handling
The application includes custom error handling for permission denials:Testing Security Rules
Using Firebase Emulator
Manual Test Cases
Test: User Cannot Access Other User's Data
Test: User Cannot Access Other User's Data
Test: User Can Create Only With Valid userId
Test: User Can Create Only With Valid userId
Deploying Security Rules
Deploy rules to Firebase:Common Security Patterns
Pattern: User Profile Creation
Pattern: Querying User Data
Pattern: Server-Side Data Access
Security Best Practices
Always Authenticate
Never allow unauthenticated access. Use
isSignedIn() in all rules.Validate Ownership
Always check
request.auth.uid matches the {userId} in the path.Immutable IDs
Make
id and userId fields immutable after creation.Test Thoroughly
Use Firebase Emulator to test rule changes before production.
Next Steps
Data Model
Explore the Firestore schema and document structure
Firebase Setup
Configure Firebase services for your application