Firestore Security Rules

Food Tracker (React + Firebase)

Chapter 6 · Firestore Security Rules

Three separate chapters have now said some version of "this isn't actually enforced yet." Chapter 2's schema is voluntary. Chapter 3's Cloud Function protects no one by simply existing. Chapter 5's client-side validation can be skipped by anyone willing to bypass the app's own JavaScript. This chapter is where that thread finally resolves — Security Rules are the one thing in this entire architecture that's genuinely, unavoidably enforced.

Rules Are Code, But Not the App's Code

Security Rules live in their own file, firestore.rules, written in a declarative rules language and deployed independently of the React app itself (firebase deploy --only firestore:rules). They're evaluated entirely on Firebase's own servers, for every single read and write, regardless of what the client's own code does or doesn't check first — precisely the gap Chapter 5's own warn-box left open.

The Default-Deny Starting Point

// firestore.rules — the production-mode default from Chapter 1 rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /items/{itemId} { allow read, write: if false; // this is exactly what caused Chapter 5's permission error } } }

Writing Real Rules for items

match /items/{itemId} { allow read: if true; // no auth yet — Chapter 11 tightens this allow create: if request.resource.data.name is string && request.resource.data.name.size() > 0 && request.resource.data.status in ['active', 'used'] && request.resource.data.addedAt == request.time; allow update, delete: if true; // tightened once auth exists — Chapter 11 }

allow read: if true is appropriately permissive for now — there's no user system yet, so there's no one to distinguish between. It gets revisited directly once Chapter 11 adds Firebase Authentication. The interesting line is create: it requires name to actually be a non-empty string, requires status to be one of exactly two valid values, and — the detail worth pausing on — requires addedAt to equal request.time, the server's own clock at the moment the write is evaluated. A client using serverTimestamp() (Chapter 5's own recommendation) satisfies this automatically; a client that tries to fake addedAt with its own local Date value gets rejected outright. Chapter 5 recommended serverTimestamp() as good practice; this rule makes it the only option that works at all.

A Different Set of Rules for barcodeCache

match /barcodeCache/{barcode} { allow read: if true; allow write: if false; // clients never write this directly — see below }

This isn't a typo — write: if false genuinely blocks every client write, and the app still works, because the only writer to this collection is Chapter 3's Cloud Function, which uses the Firebase Admin SDK rather than the regular client SDK. Admin SDK access, by design, bypasses Security Rules entirely — it runs inside Firebase's own trusted server environment, the same place a genuine secret would live if this app had one. Rules govern what the client is allowed to do; they say nothing at all about code running with Admin privileges.

The payoff chapter for a thread that's run since Chapter 2
Nothing earlier in this course was actually enforced on its own — not the data shape (Chapter 2), not the mere existence of a Cloud Function (Chapter 3), not client-side validation (Chapter 5). Security Rules are the first, and only, place in this entire architecture where something genuinely cannot be bypassed by a client, no matter how the client is modified. Everything else in this course has been building toward the moment this sentence stops being a warning and starts being true.
Test rules before they're live for everyone
The Firebase emulator set up in Chapter 1 runs Security Rules locally, letting you try a rule change against realistic reads and writes before deploying it to the actual project. The Firebase console's own Rules Playground offers a similar quick sanity check directly in the browser. Either is faster, and far safer, than editing rules and finding out what broke only after deploying to production.
The most common real Firestore mistake
Writing allow read, write: if true; everywhere "just to get it working," and forgetting to tighten it later, is the single most common real-world Firestore security failure. Unlike a bug in the app's own code, which affects however many users hit that one code path, a bad security rule potentially exposes or corrupts every document in the affected collection, for every user, the instant it's deployed. Always test a rules change in the emulator first.

Where This Course Is Headed

Expiry alerts (querying the data these rules now actually protect), item history and search, marking items used, recipe lookup, Firebase Authentication (which will directly tighten the read/update/delete rules left permissive above), deployment, and a capstone.

Hands-On Exercises

Exercise 1

Explain why `allow write: if false` was the correct starting rule from Chapter 1's own production-mode setup, and connect it directly to the permission-denied error Chapter 5 warned would happen.

📄 View solution
Exercise 2

Explain what `request.resource.data.addedAt == request.time` actually verifies, and which earlier chapter's own recommendation this rule now turns into an actual requirement.

📄 View solution
Exercise 3

Explain why Chapter 3's Cloud Function can still write to barcodeCache despite this chapter's own `allow write: if false` rule for that collection. What mechanism explains this?

📄 View solution

Chapter 6 Quick Reference

  • firestore.rules — evaluated on Firebase's servers for every read/write, deployed separately from the app
  • Default-denyif false is what caused Chapter 5's own permission error; expected, not a bug
  • addedAt == request.time — enforces that serverTimestamp() was actually used, turning Chapter 5's recommendation into a real requirement
  • Admin SDK bypasses rules entirely — exactly why Chapter 3's Cloud Function can still write to barcodeCache despite write: if false
  • This chapter's own throughline: Security Rules are the first genuinely unavoidable enforcement point in this whole course
  • Next chapter: Expiry Alerts