Step 8: Verification & Commit
Time: ~8 minutes | Type: Testing | Concepts: Security verification, Git workflow
What This Step Is About
Before moving to the next slice, we need to thoroughly test security rules and commit our code. Security is critical — bugs here mean data breaches.
Full Verification Checklist
Work through this list carefully. Security matters!
File Structure
Verify your project has these new files:
.firebaserc (new)
firebase.json (new)
firestore.rules (new)
firestore.indexes.json (new)Check:
- [ ] All files exist in project root
- [ ]
.firebaserccontains your project ID - [ ]
firebase.jsonhas firestore and hosting config - [ ]
firestore.rulescontains your security rules - [ ] No syntax errors in any files
Firebase CLI Setup
firebase --versionCheck:
- [ ] Command works (shows version number)
- [ ] Version is 12.x or 13.x
firebase projects:listCheck:
- [ ] Your project appears in list
- [ ] Project ID matches Firebase Console
Rules Deployed
firebase deploy --only firestore:rules --dry-runCheck:
- [ ] Shows "rules syntax is valid"
- [ ] No compilation errors
Check Firebase Console → Firestore → Rules:
- [ ] Rules match your local
firestore.rulesfile - [ ] "Published" timestamp is recent (within last 10 minutes)
App Functionality
npm run devCheck:
- [ ] Server starts without errors
- [ ] App loads at
http://localhost:5173 - [ ] Can log in successfully
- [ ] No errors in browser console (F12)
Security: Authentication Required
Test 1: Logged out access
- Make sure you're logged out
- Open browser console (F12)
- Try to read todos:javascript
import { collection, getDocs } from 'firebase/firestore'; import { db } from './src/lib/firebase'; await getDocs(collection(db, 'todos'));
Expected:
- [ ] Error: "Missing or insufficient permissions"
- [ ] Can't access data when not logged in
Security: User Data Isolation
Test 2: Can only read own data
- Log in as User A
- Note how many todos you have
- Check browser console (F12)
- Try to read all todos (no filter):javascript
import { collection, getDocs } from 'firebase/firestore'; import { db } from './src/lib/firebase'; const snapshot = await getDocs(collection(db, 'todos')); console.log('Todos:', snapshot.size);
Expected:
- [ ] Returns only your todos (not all users' todos)
- [ ] Count matches what you see in UI
- [ ] Even without userId filter, Firestore enforces rules
Test 3: Can't access others' data
- Log in as User A
- Get a todo ID from another user (check Firebase Console)
- Try to read it:javascript
import { doc, getDoc } from 'firebase/firestore'; import { db } from './src/lib/firebase'; const todoRef = doc(db, 'todos', 'other-users-todo-id'); await getDoc(todoRef);
Expected:
- [ ] Returns empty (no data)
- [ ] Or error: "Missing or insufficient permissions"
- [ ] Can't access other users' todos
Security: Create Validation
Test 4: Can't create invalid todos
Open browser console, try to create todo with empty title:
import { addDoc, collection, Timestamp } from 'firebase/firestore';
import { db } from './src/lib/firebase';
import { 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
Test 5: Can't create todos for other users
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
Security: Update Validation
Test 6: Can't change userId
- Get one of your todo IDs from Firebase Console
- Try to update userId:javascript
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"
- [ ] Can't change userId (immutable field)
Test 7: 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"
- [ ] Can't change createdAt (immutable field)
App CRUD Operations Still Work
Test 8: Create todo (UI)
- Go to
/todos/new - Title: "Security test todo"
- Description: "Testing with rules"
- Submit
Expected:
- [ ] Todo created successfully
- [ ] Appears in
/todoslist - [ ] Appears in Firebase Console with all fields
Test 9: Read todos (UI)
- Go to
/todos
Expected:
- [ ] All your todos appear
- [ ] Loading state shows briefly
- [ ] No permission errors in console
Test 10: Update todo (UI)
- Click "Edit" on a todo
- Change title to "Updated with rules"
- Save
Expected:
- [ ] Todo updates successfully
- [ ] New title appears in list
- [ ] No errors
Test 11: Delete todo (UI)
- Click "Delete" on a todo
- Confirm
Expected:
- [ ] Todo deleted successfully
- [ ] Disappears from list
- [ ] Removed from Firebase Console
Multi-User Isolation
Test 12: 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
- [ ] Can create own todos
Create a todo as second user
Check Firebase Console — second user's todo has different userId
Log out and log back in as first user
Go to
/todos
Expected:
- [ ] See only first user's todos
- [ ] Don't see second user's todos
Rules Playground Tests
Check Firebase Console → Firestore → Rules → Rules Playground:
- [ ] Authenticated user reading own todo: ✅ Granted
- [ ] Authenticated user reading others' todo: ❌ Denied
- [ ] Unauthenticated reading: ❌ Denied
- [ ] Create with valid data: ✅ Granted
- [ ] Create with empty title: ❌ Denied
- [ ] Create with wrong userId: ❌ Denied
Common Issues Found During Verification
All operations get "Permission denied"
Problem: Rules too restrictive or wrong collection name.
Fix:
- Check that collection name in rules matches your code (
todos) - Verify
request.auth != null(are you logged in?) - Check browser console for exact error
Can see other users' todos
Problem: Rules not enforcing userId check.
Fix:
// In firestore.rules, make sure you have:
allow read: if request.auth != null
&& resource.data.userId == request.auth.uid;Can create todos with invalid data
Problem: Validation not strict enough.
Fix: Check isValidTodo() function includes all validations:
function isValidTodo() {
let data = request.resource.data;
return data.title is string
&& data.title.size() > 0
&& data.title.size() <= 200
&& data.completed is bool
&& data.userId == request.auth.uid
&& data.createdAt is timestamp;
}Can change userId or createdAt
Problem: Update rule missing immutability check.
Fix:
allow update: if isAuthenticated()
&& isOwner()
&& isValidTodo()
&& request.resource.data.userId == resource.data.userId
&& request.resource.data.createdAt == resource.data.createdAt;Understanding Check
Before moving on, make sure you can answer these:
💡 Ask yourself:
- Can a logged-out user read any todos? (No — authentication required)
- Can User A read User B's todos, even with DevTools? (No — server-side rules block it)
- What happens if I try to create a todo with empty title? (Rejected by rules, permission denied)
- Can I change a todo's userId after creation? (No — immutable field)
- Where do security rules run? (Google's servers, not the browser)
- Can rules be bypassed by modifying React code? (No — server enforces them)
- What error message appears when rules deny a request? ("Missing or insufficient permissions")
If you can't answer these, review:
- Concepts — Security rules explained
- Step 1: Understanding Security
- Step 4: Understanding Rules Syntax
Commit Your Work
IMPORTANT: Only commit if all verification passes!
Check Git Status
git statusYou should see new files:
.firebaserc(new)firebase.json(new)firestore.rules(new)firestore.indexes.json(new)
Stage 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"Why this commit message?
- First line summarizes the feature (< 50 chars)
- Blank line separates subject from body
- Bullet points detail all security measures added
- Focuses on "what" security we added
Verify Commit
git log -1Should show your commit with the message above.
What You Accomplished
Congratulations! You now have:
✅ Firebase CLI installed and configured ✅ Firebase project initialized locally ✅ Comprehensive Firestore security rules ✅ Authentication required for all operations ✅ User data isolation enforced server-side ✅ Field validation (types, required fields, length limits) ✅ Immutability rules (userId, createdAt can't change) ✅ Rules tested in Playground ✅ Rules deployed to production ✅ App tested with rules enabled ✅ All CRUD operations still work ✅ Multi-user isolation verified ✅ All code committed to Git
Skills You Learned
Security Concepts:
- Client-side vs server-side validation
- Principle of least privilege
- User data isolation
- Attack scenarios and how to prevent them
- Testing security before deployment
Firestore Rules:
- Rules syntax (
match,allow,if) - Helper functions for DRY rules
requestvsresourceobjects- Authentication checks
- Ownership validation
- Data type validation
- Immutability enforcement
Firebase CLI:
- Installing and logging in
- Initializing Firebase in a project
- Deploying rules
- Validating rules syntax
- Testing with
--dry-run
Testing:
- Rules Playground for pre-deployment testing
- Security testing in browser console
- Multi-user testing
- Verification checklists
Next Slice
Security rules are deployed! Your app now has server-side protection. Users can only access their own data, even if they try to bypass the UI.
Next, we'll add polish to make the app production-ready:
In Slice 5, you'll add:
- Loading states and error handling
- Input validation and user feedback
- Responsive design
- Accessibility improvements
- Performance optimizations