Chapter 04 · CRUD with Client and Server SDKs: Reads, Writes, Updates, Deletes, Preconditions, and Converters

Server vs Client SDK Authentication / Authorization Models and Trusted vs Untrusted Execution

Separate untrusted mobile/web requests enforced by Firebase Authentication and Security Rules from trusted server/Admin requests governed by IAM and application authorization, then prove the difference in the emulator.

Beginner → Advanced105–135 minutesAtlasMart emulator-first CRUD labFirebase CLI 15.30.0 · Web SDK 12.19.0 · Admin Node 14.4.0 · Node.js 22+Firestore Standard Native Core semantics unless explicitly labeled EnterpriseLast reviewed: September 2026

Learning outcomes

AtlasMart exposes product/profile functionality to browsers while also running trusted backend jobs. Both can call Firestore, but they do not pass through the same authorization mechanism. Treating Admin SDK code as “the same Firestore client with more methods” is a security design error: mobile/web requests are evaluated by Firestore Security Rules, whereas Admin/server libraries use privileged credentials/IAM and bypass those Rules.

01

Trace a Web SDK request through Firebase Authentication and Firestore Security Rules.

02

Trace an Admin/server request through the privileged credential/IAM path and explain why Rules do not protect it.

03

Separate authentication, authorization, validation, and app attestation rather than treating them as synonyms.

04

Prove rule denial and server bypass behavior locally without embedding service-account keys in client code.

05

Design a trusted backend so privileged database access is narrower than “any server request may write anything.”

Chapter 04 baseline reviewed 15 September 2026

The lab continues Chapters 01–03 without changing the environment: project demo-atlasmart-firestore; Firestore emulator 127.0.0.1:8080; Auth emulator 127.0.0.1:9099; Emulator UI 127.0.0.1:4000; Firebase CLI 15.30.0; Firebase JavaScript SDK 12.19.0; Firebase Admin Node.js SDK 14.4.0 (which currently carries @google-cloud/firestore 9.1.0); Node.js 22 or newer. The default teaching surface is Firestore Standard edition, Native mode, Core operations, default database (default), emulator-first. Enterprise/Pipeline/MongoDB-compatibility behavior is labeled rather than generalized.

Trust, execution, and evidence note

Browser/mobile SDK requests are untrusted requests evaluated by Firebase Authentication context plus Cloud Firestore Security Rules. Admin/server client libraries are privileged and bypass Firestore Security Rules; production access is governed by IAM/ADC and by authorization logic in your server application. The emulator demonstrates these trust paths and CRUD semantics, but it does not prove production IAM, regional latency, billing, quotas, contention, or service availability. Expected failures are described by invariant/error family rather than fabricated captured output.

1. The same document path can have two different trust paths

A browser using the Firebase Web SDK is an untrusted client. Firebase Authentication can establish who the user is; Security Rules decide whether that authenticated request may read/write the target documents and can validate incoming fields. App Check can help attest that requests originate from an authorized app instance, but it is not user authorization. A trusted Node backend using the Admin SDK or a Google Cloud server client authenticates with privileged credentials and IAM; Firestore Security Rules are bypassed.

Question Web/mobile path Trusted server/Admin path
Who is the caller? Firebase Auth user token (when signed in) Service/workload identity via ADC/IAM plus application caller identity
Who authorizes Firestore request? Security Rules IAM authorizes database access; server code must enforce business authorization
Do Firestore Rules run? Yes No
Can privileged credentials be shipped to browser? No Keep only in trusted environment; prefer ADC/keyless workload identity where supported
Can the server trust client-supplied role/owner fields? No No; server must derive/verify authorization context

2. Minimal client Rules: protect ownership and server-managed fields

Chapter 12 will develop Security Rules systematically. For CRUD today, use a narrow rule sufficient to prove the trust boundary. AtlasMart lets a signed-in user read and update their own profile but prevents the client from changing isStaff or schemaVersion. The backend may migrate those fields because it is a privileged path—but only after its own caller authorization and input validation.

Firestore Rules · narrow Chapter 04 profile contract
rules_version = "2";service cloud.firestore {  match /databases/{database}/documents {    match /profiles/{uid} {      allow read: if request.auth != null && request.auth.uid == uid;      allow create: if request.auth != null        && request.auth.uid == uid        && request.resource.data.keys().hasOnly(["displayName", "locale", "preferences", "schemaVersion"])        && request.resource.data.schemaVersion == 2;      allow update: if request.auth != null        && request.auth.uid == uid        && request.resource.data.diff(resource.data).affectedKeys()             .hasOnly(["displayName", "locale", "preferences"]);      allow delete: if request.auth != null && request.auth.uid == uid;    }  }}

This is intentionally a Chapter 04 teaching rule, not a finished production policy. Later security chapters add type/length validation, query compatibility, claims, App Check, helper functions, adversarial tests, access-call accounting, and incident considerations.

3. Prove client denial through the emulator

Continue the same local AtlasMart workspace. Keep firebase emulators:start --only auth,firestore running. Admin/server examples set FIRESTORE_EMULATOR_HOST=127.0.0.1:8080 and GCLOUD_PROJECT=demo-atlasmart-firestore; browser examples connect the modular Web SDK explicitly to the Firestore/Auth emulators. Use only synthetic Chapter 04 documents such as profiles/u-alice, products/p-1001, crudOperations/<operationId>, and named test users. Cleanup must target only those paths.

Web SDK · attempt an allowed field and a forbidden server-managed field
import { doc, updateDoc } from "firebase/firestore";const mine = doc(db, "profiles", currentUser.uid);await updateDoc(mine, { locale: "fa" });       // expected allowed when Rules matchawait updateDoc(mine, { isStaff: true });       // expected denied by Rulesawait updateDoc(doc(db, "profiles", "u-bob"), { locale: "fa" }); // expected denied

Record the identity (uid), path, attempted fields, and allow/deny outcome. A denial proves the tested request was rejected under the active emulator rules; it does not prove every production rule path is correct. Use Rules unit tests for repeatable matrices instead of relying only on manual UI behavior.

4. Prove that Admin/server code bypasses those Rules

Point the Admin SDK at the same emulator and update isStaff. The privileged write succeeds because Firestore Security Rules are not evaluated for server client libraries. This is not a flaw; it is the intended separation. The security boundary moves into IAM and your server's own authorization logic.

Admin Node · privileged write with explicit application authorization gate
import { initializeApp } from "firebase-admin/app";import { getFirestore, FieldValue } from "firebase-admin/firestore";initializeApp({ projectId: "demo-atlasmart-firestore" });const db = getFirestore();async function setStaffFlag({ actor, targetUid, enabled, correlationId }) {  if (!actor?.roles?.includes("admin")) {    throw Object.assign(new Error("forbidden"), { code: "APP_FORBIDDEN" });  }  const ref = db.doc(`profiles/${targetUid}`);  const result = await ref.update({    isStaff: enabled,    serverUpdatedAt: FieldValue.serverTimestamp()  });  console.log(JSON.stringify({ correlationId, path: ref.path, updateTime: result.updateTime.toDate().toISOString() }));}

The important line is the application authorization gate, not the database method. In production, actor must come from a verified request/session/token or trusted job identity, never from an arbitrary JSON body saying {"role":"admin"}.

5. IAM answers “may this workload access Firestore,” not every product rule

IAM should grant the backend only the database permissions its workload needs. Even a correctly scoped Firestore IAM role generally does not know that “seller A may edit only products owned by seller A” unless your architecture maps that product rule into separate identities/resources. Most product authorization still belongs in trusted application code for the server path. Use structured authorization functions and tests, not scattered if statements.

Credential rule

Do not commit service-account private keys, embed them in HTML/JavaScript, or paste them into course fixtures. On Google-hosted environments prefer Application Default Credentials/workload identity. The emulator path in this chapter needs no production secret.

6. Failure taxonomy: rule denial is not IAM denial

Failure Typical boundary Evidence to log Repair direction
Client permission denied Security Rules uid, path, operation, rule test case—not secrets Fix Rules/query/data contract or deny intentionally
Server IAM permission denied Workload identity / IAM service identity, resource, operation, correlation ID Least-privilege IAM/configuration
Server application forbidden Business authorization verified actor ID/role, target ownership, policy decision Product authorization policy
Validation failure Rules or server validator field/path/schema error, safe reason code Fix payload/client/schema

7. Deliberately wrong approach: “Admin SDK is protected by my Firestore Rules”

A backend accepts arbitrary path and data from the browser, then calls Admin SDK set(), assuming Firestore Rules will block forbidden fields. They will not. The backend has converted an untrusted request into a privileged database mutation and potentially bypassed the client security model. Repair it by defining server-owned endpoints/use cases, authenticating the caller, authorizing the business action, validating a narrow schema, deriving server-managed fields, and using least-privilege workload credentials.

Knowledge check

Check your understanding

  1. Which mechanism authorizes a normal Firestore Web SDK request?
  2. Do Firestore Security Rules protect writes performed by Admin/server client libraries?
  3. Why is App Check not a replacement for user authorization?
  4. What must a privileged backend do before writing user-requested changes?
  5. Why should rule denial and IAM denial be logged as different failure classes?
Review the answers

1. Firebase Authentication can establish user identity, while Firestore Security Rules evaluate whether the mobile/web request is allowed and valid.

2. No. Server client libraries bypass Firestore Security Rules and authenticate/authorize with IAM/ADC; business authorization remains the application's responsibility.

3. App Check attests the calling app/device context to reduce abuse; it does not decide which authenticated user owns a document or may perform a business action.

4. Verify the caller, authorize the specific use case, validate/normalize the payload, derive privileged fields server-side, then perform the database mutation.

5. They occur at different trust layers and have different causes/remediation. Collapsing them into “permission error” makes diagnosis and security review weaker.

Summary and next step

Client and server SDKs can touch the same Firestore path while crossing different trust boundaries. Browser/mobile requests depend on Auth + Rules; server/Admin code bypasses Rules and therefore must combine IAM with explicit application authorization/validation. Next, encode the data contract itself so typed models do not become a false sense of schema safety.

Next: Data Converters/Typed Models, Serialization, Validation, and Backward-Compatible Schema Evolution.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.