Slice 4 Verification Checklist
Use this checklist before moving to Slice 5
This is your final quality check for Firestore security rules. Work through each section carefully. Security bugs mean data breaches — don't skip this!
Quick Links
If you find issues, these steps can help:
- Step 1: Understanding Security — Why rules matter
- Step 2: Install Firebase CLI — CLI setup
- Step 3: Initialize Firebase Project — Project config
- Step 4: Understanding Rules Syntax — Rules language
- Step 5: Create Security Rules — Writing rules
- Step 6: Create Firebase Config — firebase.json
- Step 7: Test & Deploy Rules — Testing and deployment
1. File Structure
project-root/
├── .firebaserc
├── firebase.json
├── firestore.rules
├── firestore.indexes.json
└── src/
└── (your app files)Verify:
- [ ] All Firebase files exist in project root (not in
src/) - [ ]
.firebasercis present - [ ]
firebase.jsonis present - [ ]
firestore.rulesis present - [ ]
firestore.indexes.jsonis present
2. Firebase CLI
Installation
firebase --versionVerify:
- [ ] Command works (not "command not found")
- [ ] Version is 12.x or 13.x or higher
- [ ] No errors
Authentication
firebase projects:listVerify:
- [ ] Your project appears in list
- [ ] Project ID matches Firebase Console
- [ ] Project name is correct
- [ ] No authentication errors
Project Configuration
firebase useVerify:
- [ ] Shows "Active Project: your-project-id"
- [ ] Project ID matches your Firebase project
3. Firebase Configuration Files
.firebaserc
cat .firebasercVerify:
- [ ] File contains JSON
- [ ] Has
"projects"key - [ ] Has
"default"key with your project ID - [ ] No syntax errors
Expected structure:
{
"projects": {
"default": "your-project-id"
}
}firebase.json
cat firebase.jsonVerify:
- [ ] File contains JSON
- [ ] Has
"firestore"section - [ ]
"rules"points to"firestore.rules" - [ ]
"indexes"points to"firestore.indexes.json" - [ ] Optionally has
"hosting"section - [ ] No syntax errors
Expected structure:
{
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"hosting": {
"public": "dist",
"ignore": [...],
"rewrites": [...]
}
}4. Security Rules File
Rules Exist
cat firestore.rulesVerify:
- [ ] File is not empty
- [ ] Contains
rules_version = '2'; - [ ] Contains
service cloud.firestore - [ ] Contains
match /todos/{todoId} - [ ] Has rules for read, create, update, delete
- [ ] No default
if falserules
Rules Syntax Valid
firebase deploy --only firestore:rules --dry-runVerify:
- [ ] Shows "rules syntax is valid"
- [ ] Shows "rules file firestore.rules compiled successfully"
- [ ] No compilation errors
- [ ] No syntax errors
5. Rules Deployed
Check Firebase Console
- Go to Firebase Console
- Select your project
- Go to Firestore Database → Rules tab
Verify:
- [ ] Rules are shown in the editor
- [ ] "Published" timestamp is recent (within last hour)
- [ ] Rules match your local
firestore.rulesfile - [ ] No "if false" default rules
Check Deployment
firebase deploy --only firestore:rulesVerify:
- [ ] Shows "Deploy complete!"
- [ ] No errors
- [ ] Rules uploaded successfully
- [ ] Firebase Console updates immediately
6. Development Server
npm run devVerify:
- [ ] Server starts without errors
- [ ] Terminal shows "Local: http://localhost:5173/"
- [ ] No red error messages in terminal
- [ ] Opening browser loads the app
- [ ] Browser console (F12) shows no errors
- [ ] Authentication still works (can log in)
7. App Functionality with Rules
Authentication
Logged out:
- [ ] Can access home page
- [ ] Can access login page
- [ ] Can access register page
- [ ] Cannot access
/todos(redirects to login)
Logged in:
- [ ] Can access
/todos - [ ] Can access
/todos/new - [ ] Can access
/todos/edit/:id - [ ] Dashboard works
Create Operation
Valid todo:
- Go to
/todos/new - Enter title: "Test with rules"
- Enter description: "Security rules active"
- Click submit
Expected:
- [ ] Todo created successfully
- [ ] Redirects to
/todos - [ ] New todo appears in list
- [ ] No errors in browser console
- [ ] Firebase Console shows todo with all fields:
- title
- description
- completed: false
- userId: (your user ID)
- createdAt: (timestamp)
Invalid todo (empty title):
- [ ] Try to submit with empty title
- [ ] Shows validation error
- [ ] Does not submit to Firestore
Read Operation
View todos:
- Go to
/todos
Expected:
- [ ] Brief loading state
- [ ] Your todos appear
- [ ] All fields display correctly
- [ ] No "Permission denied" errors in console
- [ ] Only YOUR todos appear (not other users')
Update Operation
Valid update:
- Click "Edit" on a todo
- Change title to "Updated with rules"
- Save
Expected:
- [ ] Todo updates successfully
- [ ] New title appears in list
- [ ] No errors in console
- [ ] Firebase Console shows updated title
- [ ] userId and createdAt unchanged
Delete Operation
Valid delete:
- Click "Delete" on a todo
- Confirm deletion
Expected:
- [ ] Todo deleted successfully
- [ ] Disappears from list
- [ ] Removed from Firebase Console
- [ ] No errors in console
8. Security: Authentication Required
Logged Out Access Blocked
Test in browser console (while logged out):
import { collection, getDocs } from 'firebase/firestore';
import { db } from './src/lib/firebase';
await getDocs(collection(db, 'todos'));Expected:
- [ ] Error: "Missing or insufficient permissions"
- [ ] Cannot access data when not authenticated
- [ ] Request rejected by Firestore
9. Security: User Data Isolation
Can Only Read Own Data
Test 1: Query without userId filter
Log in and run in browser console:
import { collection, getDocs } from 'firebase/firestore';
import { db } from './src/lib/firebase';
const snapshot = await getDocs(collection(db, 'todos'));
console.log('Todos count:', snapshot.size);Expected:
- [ ] Returns only YOUR todos
- [ ] Count matches what you see in UI
- [ ] Even without userId filter, rules enforce isolation
- [ ] Can't access other users' data
Can't Read Others' Data
Test 2: Try to read another user's todo
- Get a todo ID from another user (check Firebase Console)
- Run in browser console:
import { doc, getDoc } from 'firebase/firestore';
import { db } from './src/lib/firebase';
const todoRef = doc(db, 'todos', 'other-users-todo-id');
const snapshot = await getDoc(todoRef);
console.log('Data:', snapshot.exists());Expected:
- [ ]
snapshot.exists()isfalse - [ ] Or error: "Missing or insufficient permissions"
- [ ] Cannot access other users' documents
Multi-User Test
Test 3: Second user can't see first user's data
- Log out
- Create a new user account (different email)
- Go to
/todos
Expected:
- [ ] Empty state appears
- [ ] Don't see first user's todos
Create a todo as second user
Check Firebase Console:
- [ ] Second user's todo has different userId
- [ ] Both users' todos exist in database
Log out and log in as first user
Go to
/todos
Expected:
- [ ] See only first user's todos
- [ ] Don't see second user's todos
10. Security: Create Validation
Can't Create Invalid Todos
Test 1: Empty title
Run in browser console:
import { addDoc, collection, Timestamp } from 'firebase/firestore';
import { db, auth } from './src/lib/firebase';
await addDoc(collection(db, 'todos'), {
title: '', // Empty!
completed: false,
userId: auth.currentUser.uid,
createdAt: Timestamp.now()
});Expected:
- [ ] Error: "Missing or insufficient permissions"
- [ ] Empty title rejected by rules
- [ ] No document created in Firestore
Test 2: Missing required fields
await addDoc(collection(db, 'todos'), {
title: 'Test',
completed: false
// Missing userId and createdAt!
});Expected:
- [ ] Error: "Missing or insufficient permissions"
- [ ] Missing fields rejected by rules
Test 3: Wrong userId
await addDoc(collection(db, 'todos'), {
title: 'Spam',
completed: false,
userId: 'fake-user-id', // Wrong userId!
createdAt: Timestamp.now()
});Expected:
- [ ] Error: "Missing or insufficient permissions"
- [ ] Wrong userId rejected by rules
- [ ] Can't create todos for other users
Test 4: Wrong data types
await addDoc(collection(db, 'todos'), {
title: 123, // Number instead of string!
completed: 'yes', // String instead of boolean!
userId: auth.currentUser.uid,
createdAt: Timestamp.now()
});Expected:
- [ ] Error: "Missing or insufficient permissions"
- [ ] Wrong types rejected by rules
11. Security: Update Validation
Can't Change Immutable Fields
Test 1: Can't change userId
- Get one of your todo IDs from Firebase Console
- Run in browser console:
import { doc, updateDoc } from 'firebase/firestore';
import { db } from './src/lib/firebase';
const todoRef = doc(db, 'todos', 'your-todo-id');
await updateDoc(todoRef, {
userId: 'different-user' // Try to reassign!
});Expected:
- [ ] Error: "Missing or insufficient permissions"
- [ ] userId cannot be changed (immutable)
Test 2: Can't change createdAt
import { doc, updateDoc, Timestamp } from 'firebase/firestore';
import { db } from './src/lib/firebase';
const todoRef = doc(db, 'todos', 'your-todo-id');
await updateDoc(todoRef, {
createdAt: Timestamp.now() // Try to change date!
});Expected:
- [ ] Error: "Missing or insufficient permissions"
- [ ] createdAt cannot be changed (immutable)
Can Update Mutable Fields
Test 3: Can update title and completed
const todoRef = doc(db, 'todos', 'your-todo-id');
await updateDoc(todoRef, {
title: 'Updated title',
completed: true
});Expected:
- [ ] Update succeeds
- [ ] Changes appear in Firebase Console
- [ ] No errors
12. Security: Delete Validation
Can Only Delete Own Todos
Test 1: Delete own todo
import { doc, deleteDoc } from 'firebase/firestore';
import { db } from './src/lib/firebase';
const todoRef = doc(db, 'todos', 'your-todo-id');
await deleteDoc(todoRef);Expected:
- [ ] Delete succeeds
- [ ] Todo removed from Firestore
- [ ] No errors
Test 2: Can't delete others' todos
- Get another user's todo ID from Firebase Console
- Try to delete:
const todoRef = doc(db, 'todos', 'other-users-todo-id');
await deleteDoc(todoRef);Expected:
- [ ] Error: "Missing or insufficient permissions"
- [ ] Todo not deleted
- [ ] Still exists in Firestore
13. Rules Playground Tests
Check Firebase Console → Firestore → Rules → Rules Playground:
Test 1: Read Own Todo
- Operation:
get - Path:
/todos/test123 - Authenticated: ✅ Yes
- Firebase UID:
user123 - Simulate document:
{"userId": "user123", "title": "Test"}
Expected:
- [ ] ✅ Access granted
Test 2: Read Others' Todo
- Operation:
get - Path:
/todos/test456 - Authenticated: ✅ Yes
- Firebase UID:
user123 - Simulate document:
{"userId": "user456", "title": "Test"}
Expected:
- [ ] ❌ Access denied
Test 3: Unauthenticated Read
- Operation:
get - Path:
/todos/test123 - Authenticated: ❌ No
Expected:
- [ ] ❌ Access denied
Test 4: Create Valid Todo
- Operation:
create - Path:
/todos/newTodo - Authenticated: ✅ Yes
- Firebase UID:
user123 - Data:
{"title": "Test", "userId": "user123", "completed": false, "createdAt": "2024-01-15T10:00:00Z"}
Expected:
- [ ] ✅ Access granted
Test 5: Create Invalid Todo (Empty Title)
- Operation:
create - Path:
/todos/newTodo - Authenticated: ✅ Yes
- Firebase UID:
user123 - Data:
{"title": "", "userId": "user123", "completed": false, "createdAt": "2024-01-15T10:00:00Z"}
Expected:
- [ ] ❌ Access denied
Test 6: Create with Wrong userId
- Operation:
create - Path:
/todos/newTodo - Authenticated: ✅ Yes
- Firebase UID:
user123 - Data:
{"title": "Test", "userId": "user456", "completed": false, "createdAt": "2024-01-15T10:00:00Z"}
Expected:
- [ ] ❌ Access denied
14. Code Quality
Lint Check
npm run lintVerify:
- [ ] No errors in Firebase config files
- [ ] No errors in app code
- [ ] Rules file is properly formatted
TypeScript Check
npx tsc --noEmitVerify:
- [ ] No TypeScript compilation errors
- [ ] All imports resolve correctly
Console Check
Open browser console (F12):
- [ ] No red errors on page load
- [ ] No errors during CRUD operations
- [ ] No Firestore permission errors (unless expected)
Specific things to avoid:
- ❌ "Missing or insufficient permissions" (unless testing security)
- ❌ "Cannot read properties of undefined"
- ❌ "Collection not found"
- ❌ Unexpected errors
15. Understanding Check
Before moving on, make sure you can answer these:
- [ ] Why can't I trust client-side validation? (Users control the browser, can bypass it)
- [ ] Where do Firestore security rules run? (Google's servers, not the browser)
- [ ] Can a user bypass rules with DevTools? (No, rules are server-side)
- [ ] What does
request.auth.uidcontain? (Current user's unique ID) - [ ] What's the difference between
request.resource.dataandresource.data? (New data vs existing data) - [ ] Can I change a todo's userId after creation? (No, immutable field)
- [ ] What happens if rules deny a request? (Error: "Missing or insufficient permissions")
- [ ] Why do we validate data in rules if we validate in React? (Client-side can be bypassed, server-side can't)
If you can't answer these, review:
- Concepts — Security explained
- Step 1: Understanding Security
- Step 4: Understanding Rules Syntax
16. Git Commit
Before committing:
- [ ] All checks above pass
- [ ] Rules deployed and tested
- [ ] App works with rules enabled
- [ ] No console errors
- [ ] Security verified
Check status:
git statusStage changes:
git add .firebaserc firebase.json firestore.rules firestore.indexes.jsonCommit:
git commit -m "Add Firestore security rules
- Initialize Firebase CLI in project
- Create security rules for todos collection
- Require authentication for all operations
- Enforce user data isolation via userId checks
- Validate required fields and data types
- Prevent changing immutable fields (userId, createdAt)
- Test rules in Rules Playground
- Deploy rules to production
- Verify app functionality with rules enabled"Verify commit:
git log -1Should show your commit message.
17. Final Checks
You're ready for Slice 5 if:
- [ ] ✅ Firebase CLI installed and logged in
- [ ] ✅ Firebase project initialized locally
- [ ] ✅ Security rules written and deployed
- [ ] ✅ Authentication required for all operations
- [ ] ✅ User data isolation enforced (can't access others' data)
- [ ] ✅ Create validation (empty title, wrong userId blocked)
- [ ] ✅ Update validation (can't change userId or createdAt)
- [ ] ✅ Delete validation (can only delete own todos)
- [ ] ✅ Rules tested in Playground (all tests pass)
- [ ] ✅ App still works with rules enabled
- [ ] ✅ No console errors
- [ ] ✅ Multi-user testing passed
- [ ] ✅ All code committed to Git
- [ ] ✅ You understand how rules protect your data
Troubleshooting
"Missing or insufficient permissions" for valid operations
Problem: Rules too restrictive or data missing required fields.
Check:
- Firebase Console → Firestore → Rules → Logs
- See which rule is denying the request
- Check if document has
userIdfield - Check if
userIdmatches current user
Fix:
// Make sure your todos have userId
const todos = await getDocs(query(
collection(db, 'todos'),
where('userId', '==', currentUser.uid)
));Rules allow access they shouldn't
Problem: Rules not restrictive enough.
Check:
- Firebase Console → Firestore → Rules
- Make sure you have
isOwner()checks - Make sure you're checking
request.auth != null
Fix:
// Read rule should check ownership
allow read: if request.auth != null
&& resource.data.userId == request.auth.uid;Can't create todos at all
Problem: Validation too strict or missing fields.
Check:
- Browser console for exact error
- Make sure you're setting all required fields:
- title (string, 1-200 chars)
- completed (boolean)
- userId (matches auth.currentUser.uid)
- createdAt (Timestamp)
Fix:
await addDoc(collection(db, 'todos'), {
title,
description,
completed: false,
userId: currentUser.uid, // Must match!
createdAt: Timestamp.now() // Must be Timestamp!
});Syntax errors in rules
Problem: Typo or wrong syntax.
Fix:
# Check syntax
firebase deploy --only firestore:rules --dry-run
# Common issues:
# - Missing semicolons
# - Unmatched brackets {}
# - Using = instead of ==
# - Wrong property names (request.user instead of request.auth)Next Steps
If all checks pass, you're ready to move on!
What you accomplished:
- ✅ Installed and configured Firebase CLI
- ✅ Initialized Firebase in your project
- ✅ Created comprehensive security rules
- ✅ Enforced authentication and user isolation
- ✅ Validated data types and required fields
- ✅ Tested rules thoroughly
- ✅ Deployed rules to production
- ✅ Verified app works with security enabled
Next slice:
In Slice 5, you'll add:
- Better error handling and user feedback
- Loading states throughout the app
- Responsive design for mobile
- Accessibility improvements
- Performance optimizations
- Final touches before deployment