NShop
Multi-tenancy

Isolation

Exactly which data is shared across tenants and which isn't.

Shared across tenants

  • Customers (Firebase Auth users). One account works on every store.
  • The /users/{uid} doc. Profile, addresses, wishlist — they follow the customer, not the store.
  • Reviews. A customer's reviews are written under their identity; the product they review belongs to a tenant, but the review itself is global.

Isolated per tenant

  • Products and variants (/products/{productId} + subcollections). The rule requires request.resource.data.tenantId === auth.token.tenantId on create and a matching tenant on update.
  • Orders (/orders/{orderId}). Each order doc lists tenantIds: string[]. Tenant admins can read orders that include their tenant; the customer can read their own.
  • Categories, coupons, shipping rates, branding, legal pages, settings — all per tenant.
  • Push notification tokens/admin_push_tokens/* is keyed by tenant.

Server-only collections

These collections deny all client reads and writes — only the Admin SDK (server code) touches them:

  • /mail — outbound email queue.
  • /reservations — checkout stock holds.
  • /pending_status_emails — debounce queue for status-change emails.
  • /coupons — read by checkout, written by admins through API routes.
  • /admin_invites — pending team invites.
  • /admin_audit — append-only audit log of team changes.
  • /privacy_audit — append-only audit log of GDPR deletions.

What server routes must always do

Every mutating API route:

  1. Verifies the session or bearer token.
  2. Re-reads the auth claims server-side.
  3. Compares the tenantId claim against the request's resolved tenant.
  4. Performs the write through the Admin SDK.

The Firestore rule is the second line of defense, not the first. The route is responsible for refusing requests before the database does.

On this page