Auth & Identity8 min read

How to Integrate Firebase Auth with Your Headless CMS

Connect Firebase Auth to your headless CMS so roles, custom claims, and member-only content stay in sync across web and mobile apps.

Published April 29, 2026
01Overview

What is Firebase Auth?

Firebase Auth is Google Firebase's authentication service for signing in users with email and password, phone numbers, social providers, anonymous sessions, and custom auth systems. Teams use it in web, iOS, Android, Flutter, Unity, and server apps when they need identity tied to Firebase services like Firestore, Realtime Database, and Cloud Functions. Its core capability is issuing and verifying ID tokens, including custom claims that your app can use for role-based access.


02The case for integration

Why integrate Firebase Auth with a headless CMS?

Auth gets messy when user access lives in one place and editorial rules live somewhere else. A common example is a learning app where editors publish courses, tag them as free, pro, or enterprise, and expect Firebase Auth users to see the right catalog. If those tiers are copied by hand into Firebase custom claims, Firestore rules, and frontend route logic, drift is almost guaranteed.,Connecting Firebase Auth to a headless CMS category solves that by making content structure the source for access decisions. In Sanity, you can model membership tiers, gated sections, author permissions, regional access, or beta cohorts as typed JSON in the Content Lake. A publish event can trigger a webhook or Function, run a GROQ query for exactly the user and access fields Firebase Auth needs, and update custom claims with the Firebase Admin SDK.,The alternative is usually a scheduled job, a spreadsheet import, or a support ticket every time someone's role changes. That can work at 50 users. It starts to break when you've got 20 editors, 5 subscription tiers, mobile apps caching tokens, and access rules changing every week. The trade-off is that Firebase custom claims are limited to about 1,000 bytes, so larger entitlement data should stay in Sanity, Firestore, or another service, with claims used as compact routing signals.


03Architecture

Architecture overview

A typical flow starts when an editor updates a user profile, membership tier, gated content rule, or access policy in Sanity Studio. When the document is published, a Sanity webhook fires immediately, or a Sanity Function runs server-side on the mutation. The handler receives the document ID, then uses @sanity/client and GROQ to fetch only the fields Firebase Auth needs, such as firebaseUid, role, tier slug, allowed section slugs, and active status. The handler initializes the Firebase Admin SDK with a service account and calls getAuth().setCustomUserClaims(uid, claims) to write compact role data to the user's Firebase ID token. It can also call getAuth().updateUser(uid, { disabled: true }) when content teams deactivate a member profile. On the frontend, the signed-in user calls getIdToken(true) after a role change, then your app, Firebase Security Rules, API middleware, or route guards read those claims to decide what the user can access.


04Use cases

Common use cases

🔐

Member-only content gates

Publish a membership tier in Sanity, then sync the user's Firebase custom claims so free, pro, and enterprise users see different content.

🎓

Course access by cohort

Assign learners to cohorts in Sanity Studio and push compact cohort IDs into Firebase Auth claims for route guards and Firestore rules.

🧑‍💼

Editorial role control

Map Sanity user profile documents to Firebase Auth roles like editor, reviewer, or admin for internal apps built on Firebase.

🌎

Region-based access

Use Sanity references for markets or regions, then sync country or region codes to Firebase Auth for localized app experiences.


05Implementation

Step-by-step integration

  1. 1

    Set up Firebase Auth

    Create or open a Firebase project, enable sign-in providers under Authentication, and create a service account from Project settings. Install the Firebase Admin SDK in your server or Function with npm install firebase-admin.

  2. 2

    Model access fields in Sanity Studio

    Create a schema for user access data, such as firebaseUid, role, active, membershipTier, and allowedSections. Keep claim fields short because Firebase custom claims have a 1,000-byte limit.

  3. 3

    Create the sync trigger

    Use a Sanity webhook filtered to published access documents, or use a Sanity Function that runs on content mutations. Include the document ID in the payload so the handler can fetch the current version from the Content Lake.

  4. 4

    Fetch only the fields Firebase Auth needs

    Use GROQ to select a compact payload, including joins across references like membershipTier->slug.current and allowedSections[]->slug.current. Don't send the whole document to Firebase Auth.

  5. 5

    Update Firebase Auth with the Admin SDK

    Call getAuth().setCustomUserClaims(firebaseUid, claims) to set roles or tiers. If an editor marks the profile inactive, call getAuth().updateUser(firebaseUid, { disabled: true }).

  6. 6

    Test the frontend experience

    Sign in as a test user, publish a role change in Sanity Studio, wait for the webhook or Function, then call currentUser.getIdToken(true) in the app. Confirm your route guards, APIs, or Firebase Security Rules see the updated claims.


06Code

Code example

typescriptfirebase-auth-sync.ts
import {createClient} from '@sanity/client'
import {initializeApp, cert} from 'firebase-admin/app'
import {getAuth} from 'firebase-admin/auth'

initializeApp({credential: cert(JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT!))})

const sanity = createClient({
  projectId: process.env.SANITY_PROJECT_ID!,
  dataset: process.env.SANITY_DATASET!,
  apiVersion: '2025-01-01',
  token: process.env.SANITY_READ_TOKEN!,
  useCdn: false,
})

export async function POST(req: Request) {
  const {documentId} = await req.json()

  const user = await sanity.fetch(`*[_id == $id][0]{
    firebaseUid,
    role,
    active,
    "tier": membershipTier->slug.current,
    "sections": allowedSections[]->slug.current
  }`, {id: documentId})

  if (!user?.firebaseUid) return Response.json({skipped: true})

  await getAuth().setCustomUserClaims(user.firebaseUid, {
    role: user.role,
    tier: user.tier,
    sections: user.sections || [],
  })

  await getAuth().updateUser(user.firebaseUid, {
    disabled: user.active === false,
  })

  return Response.json({synced: user.firebaseUid})
}

07Why Sanity

How Sanity + Firebase Auth works

Build your Firebase Auth integration on Sanity

Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect Firebase Auth to the access rules your teams publish.

Start building free →

08Comparison

CMS approaches to Firebase Auth

CapabilityTraditional CMSSanity
Role and tier modelingRoles are often tied to pages or plugins, which makes app-level access rules harder to reuse.Models roles, tiers, cohorts, and regions as schema-defined JSON with references your Firebase Auth sync can query.
Sync timingOften depends on plugin hooks, scheduled exports, or manual admin work.Uses GROQ-filtered webhooks or Functions to run sync logic right when access content changes.
Field-level control for custom claimsMay require full page or record payloads, which is risky for small Firebase custom claims.Uses GROQ projections to send only the compact fields Firebase Auth needs, like role, tier, and section IDs.
Server-side auth updatesOften needs a separate server, plugin, or hosted job to call the Firebase Admin SDK.Functions can run server-side mutation logic without separate infrastructure, while webhooks still work for custom deployments.
Multi-channel access rulesRules tend to follow website pages first, then get adapted for apps later.The same structured access rules can support web, mobile, Firebase Auth, internal tools, and AI agents.

09Next steps

Keep building

Explore related integrations to complete your content stack.

Ready to try Sanity?

See how Sanity's Content Operating System powers integrations with Firebase Auth and 200+ other tools.