Step 7: Test & Deploy Rules
Time: ~10 minutes | Type: Testing & Deployment | Concepts: Rules Playground, deployment, security testing
What We're Doing
Testing security rules in the Firebase Rules Playground before deploying, then deploying them to production and verifying your app still works.
Part 1: Test in Rules Playground
IMPORTANT: Always test rules before deploying to avoid breaking your app!
Open Rules Playground
- Go to Firebase Console
- Select your project
- Click Firestore Database in sidebar
- Click Rules tab
- Click Rules Playground (top right)
You should see:
- A simulator where you can test operations
- Options to set authentication
- Ability to specify paths and data
Test Scenario 1: Authenticated User Reads Own Todo
Setup:
- Operation:
get - Path:
/todos/test123 - Authenticated: ✅ Checked
- Firebase UID:
user123 - Simulate existing document:
- Click "Run" first without this to see current rules
- Then click "Simulate read" and add:
json{ "title": "Test todo", "userId": "user123", "completed": false, "createdAt": "2024-01-15T10:00:00Z" }
Click "Run"
Expected result:
✅ Access granted
Allowed by: match /todos/{todoId}, allow readWhy: User is authenticated and owns the todo (userId matches).
Test Scenario 2: Authenticated User Reads Someone Else's Todo
Setup:
- Operation:
get - Path:
/todos/test456 - Authenticated: ✅ Checked
- Firebase UID:
user123(same user) - Simulate existing document:json
{ "title": "Someone else's todo", "userId": "user456", "completed": false, "createdAt": "2024-01-15T10:00:00Z" }
Click "Run"
Expected result:
❌ Access denied
Simulated read deniedWhy: User doesn't own this todo (userId doesn't match).
Test Scenario 3: Unauthenticated User Tries to Read
Setup:
- Operation:
get - Path:
/todos/test123 - Authenticated: ❌ Unchecked
- Simulate existing document: (same as Scenario 1)
Click "Run"
Expected result:
❌ Access denied
Simulated read deniedWhy: User is not authenticated (request.auth is null).
Test Scenario 4: Create Todo with Valid Data
Setup:
- Operation:
create - Path:
/todos/newTodo123 - Authenticated: ✅ Checked
- Firebase UID:
user123 - Data to write:json
{ "title": "New todo", "description": "Test description", "completed": false, "userId": "user123", "createdAt": "2024-01-15T10:00:00Z" }
Click "Run"
Expected result:
✅ Access granted
Allowed by: match /todos/{todoId}, allow createWhy: User is authenticated, all fields are valid, userId matches.
Test Scenario 5: Create Todo with Invalid Data
Setup:
- Operation:
create - Path:
/todos/newTodo456 - Authenticated: ✅ Checked
- Firebase UID:
user123 - Data to write:json(Note: Empty title)
{ "title": "", "completed": false, "userId": "user123", "createdAt": "2024-01-15T10:00:00Z" }
Click "Run"
Expected result:
❌ Access denied
Simulated create deniedWhy: Title is empty, fails validation (title.size() > 0).
Test Scenario 6: Create Todo for Another User
Setup:
- Operation:
create - Path:
/todos/newTodo789 - Authenticated: ✅ Checked
- Firebase UID:
user123 - Data to write:json(Note: userId doesn't match authenticated user)
{ "title": "Spam todo", "completed": false, "userId": "user456", "createdAt": "2024-01-15T10:00:00Z" }
Click "Run"
Expected result:
❌ Access denied
Simulated create deniedWhy: userId doesn't match request.auth.uid, fails validation.
Verify All Tests Pass
You should have:
- ✅ Scenario 1: Granted (own data)
- ❌ Scenario 2: Denied (others' data)
- ❌ Scenario 3: Denied (not logged in)
- ✅ Scenario 4: Granted (valid create)
- ❌ Scenario 5: Denied (invalid data)
- ❌ Scenario 6: Denied (wrong userId)
If all match expected results: ✅ Rules are working correctly!
If any fail unexpectedly:
- Check your
firestore.rulesfile - Make sure helper functions are defined correctly
- Ask AI: "My rules test failed. Expected [X], got [Y]. Here are my rules: [paste rules]"
Part 2: Deploy Rules to Firestore
Now that rules are tested, let's deploy them!
Deploy Command
firebase deploy --only firestore:rulesWhat this does:
- Uploads
firestore.rulesto your Firebase project - Replaces current rules
- Takes effect immediately
Output you'll see:
=== Deploying to 'your-project'...
i deploying firestore
i firestore: checking firestore.rules for compilation errors...
✔ firestore: rules file firestore.rules compiled successfully
i firestore: uploading rules firestore.rules...
✔ firestore: released rules firestore.rules to cloud.firestore
✔ Deploy complete!If you see "Deploy complete": ✅ Rules are live!
Verify Deployment
Check that rules are active in Firebase Console:
- Go to Firebase Console → Firestore → Rules
- You should see your new rules
- Top of page shows "Published" with timestamp
Your rules should match your local firestore.rules file.
Part 3: Test Your App with New Rules
CRITICAL: Test your app to make sure rules don't break functionality.
Start Dev Server
npm run devTest: Read Your Own Todos
- Log in to your app
- Go to
/todos - Expected: Your todos appear
- Check browser console (F12): No "Permission denied" errors
If todos appear: ✅ Read rules working!
If "Permission denied" error:
- Check that todos have
userIdfield matching your user ID - Check Firebase Console → Firestore → todos collection
- Verify
userIdmatches yourrequest.auth.uid
Test: Create New Todo
- Go to
/todos/new - Fill in title and description
- Click submit
- Expected: Todo created successfully
- Check Firebase Console: New todo appears with
userId,createdAt
If todo created: ✅ Create rules working!
If "Permission denied":
- Check that your code sets
userId: currentUser.uid - Check that your code sets
createdAt: Timestamp.now() - Check browser console for exact error
Test: Update Your Todo
- On
/todos, click "Edit" on a todo - Change title or description
- Click save
- Expected: Todo updates successfully
If update works: ✅ Update rules working!
If "Permission denied":
- Check that you're not trying to change
userId - Check that you're not trying to change
createdAt - Verify you own the todo (userId matches)
Test: Delete Your Todo
- On
/todos, click "Delete" on a todo - Confirm deletion
- Expected: Todo deleted
If delete works: ✅ Delete rules working!
Test: User Isolation (Advanced)
Optional but recommended:
Log out
Create a second user account (different email)
Go to
/todosExpected: Empty list (don't see first user's todos)
Create a todo as second user
Log out and log in as first user
Expected: See only first user's todos, not second user's
If users can't see each other's data: ✅ Isolation working!
Part 4: Test Security (Optional but Recommended)
Try to Access Others' Data (Should Fail)
Open browser console (F12) and try:
import { collection, getDocs } from 'firebase/firestore';
import { db } from './src/lib/firebase';
// Try to get all todos (no filter)
const allTodos = await getDocs(collection(db, 'todos'));
console.log('Todos fetched:', allTodos.size);Expected: Only returns your own todos, even though we didn't filter by userId!
Why: Firestore automatically filters based on rules. Even if your query doesn't filter, the server does.
Try to Create Invalid Todo (Should Fail)
import { addDoc, collection, Timestamp } from 'firebase/firestore';
import { db } from './src/lib/firebase';
// Try to create todo with empty title
await addDoc(collection(db, 'todos'), {
title: '', // Invalid!
completed: false,
userId: 'user123',
createdAt: Timestamp.now()
});Expected: Error: Missing or insufficient permissions
Why: Rules validate title.size() > 0, empty title fails.
Common Issues
"Permission denied" when reading own todos
Problem: Todos missing userId field or userId doesn't match.
Fix:
- Check Firebase Console → Firestore → todos
- Click on a todo document
- Verify it has
userIdfield - Verify
userIdvalue matches your user ID (check Firebase Authentication for your UID) - If missing, manually add
userIdfield or recreate todos
"Permission denied" when creating todos
Problem: Code not setting required fields.
Fix: Check your create code:
await addDoc(collection(db, 'todos'), {
title,
description,
completed: false,
userId: currentUser.uid, // Must match authenticated user!
createdAt: Timestamp.now() // Must be Timestamp!
});"Permission denied" when updating
Problem: Trying to change userId or createdAt.
Fix: Check your update code doesn't include:
// DON'T update these
await updateDoc(todoRef, {
userId: 'something', // Immutable!
createdAt: Timestamp.now() // Immutable!
});
// CORRECT: Only update mutable fields
await updateDoc(todoRef, {
title,
description,
completed
});Still having permission errors?
Debug steps:
- Check browser console for exact error
- Check Firebase Console → Firestore → Rules → Logs for denied requests
- Verify userId in your document matches your authenticated user
- Ask AI:
I'm getting "Permission denied" when [creating/reading/updating/deleting] todos. My code does: [paste your Firestore code] My rules are: [paste your firestore.rules] Error message: [paste error from console] What's wrong?
Understanding What You Did
💡 Ask yourself:
- Why test rules in the Playground first? (Catch errors before deploying, avoid breaking production)
- What happens when you deploy rules? (Uploaded to Firebase, enforced immediately)
- Can users bypass rules by modifying client code? (No — rules run on server)
- If a rule denies a request, what error does the user see? ("Missing or insufficient permissions")
- Do rules affect existing data? (No — only control access to data)
What You Learned
At this point you should have:
- ✅ Tested rules in Rules Playground
- ✅ Verified all test scenarios pass
- ✅ Deployed rules to production
- ✅ Tested app with new rules (all CRUD works)
- ✅ Verified user data isolation
- ✅ Understanding that rules are now enforced server-side
Next Step
Rules are deployed and tested! Now let's do comprehensive verification and commit your work: