Start by deciding who can access what
Imagine a notes app. Someone signs in, saves a few ideas and picks up where they left off on another device. A straightforward flow. But what happens if a second person discovers the address of someone else’s note? Security design starts with that question. Showing only the relevant notes on screen does not, by itself, restrict access to the data.
Firebase Authentication verifies identity. Cloud Firestore Security Rules decide when data requests from mobile and web clients are allowed. Opening every document to everyone who has signed in is unsuitable for private notes. First, write down a clear product decision: ‘A user can read and edit only their own notes.’
There is no sharing in this example. Adding collaborative notes later means separately designing membership, invitations and access removal. Rather than bringing an unnecessary sharing model into the first release, keep the initial boundary easy to understand.
Build the data model around that boundary
You might store private notes under a user-specific path: users/{uid}/notes/{noteId}. A rule checking that the user identity in the path matches the authenticated identity is an understandable starting point for this example. A document identifier being difficult to guess does not replace access control; the rules enforce the boundary.
When allowing writes, consider the shape of the data too. Is the title text, is its length acceptable, and is the client sending only permitted fields? Firestore rules support field and type validation. Look for indirect problems as well, such as granting administrator privileges based on a profile field the user can change.
A small data dictionary helps: What does each field mean, who creates it, and who can change it later? This makes it easier to see whether an interface change also affects the rules. Adding note colours may look like a simple screen update, but it should be reflected in the list of accepted fields.
Design queries around the access model too
Firestore rules do not filter results after a query. A query that could return unauthorised documents can be rejected in full. Instead of requesting all notes and separating them by user in the interface, construct queries that satisfy the access boundary. In the user-subcollection example, listing notes directly from the relevant user’s collection keeps that relationship clear.
Decide how many records the list screen actually needs. A bounded initial result set, pagination for more records and a clear empty state are usually a good starting point. Avoid treating ‘no data’ and ‘access denied’ as the same message. An instruction to sign in again should not make it harder for the developer to notice an incorrect rule.
Try the denied paths as well as the allowed ones
Firebase Local Emulator Suite lets you test rules locally. Explicitly load the rules into the test environment, then make requests using different identities. The JavaScript rules-unit-testing package helps automate these scenarios. Small, known fixtures rather than production data make it easier to understand exactly which behaviour a test checks.
Write the expected outcomes in a table and run the same scenarios whenever rules change. Rejected operations deserve particular attention: A successful save screen does not demonstrate that another account’s note is protected. Clear test data between scenarios so records left by an earlier run do not create false confidence.
- Someone who has not signed in should be unable to read a private note.
- A user should be able to create and edit their own note.
- That user should be unable to read or change another account’s note.
- Unexpected fields and invalid data types should be rejected.
App Check adds a layer; the server has a separate boundary
App Check uses app or device attestation to help reduce abuse of supported services. It does not decide which note a user can access, and it does not replace Authentication or rules. It cannot guarantee protection against every form of abuse either. Think of these tools as layers with different responsibilities.
When using the Admin SDK or Firestore server libraries, Security Rules are bypassed and IAM permissions govern access. A server endpoint must therefore separately verify that the requesting user is authorised to perform the operation. Even for something as simple as deleting a note, do not blindly trust an identity supplied in the request.
A small checklist can be enough for a first release: Is the data boundary clear, have rejected requests been tested, and does the interface explain errors? Return to these questions when adding features. A safer start comes from keeping changes understandable and verifiable, then building on that foundation.
Sources and further reading
Want to dig a little deeper? Here are the official docs behind this article.
