Lesson 6 / 25

Auth State, ID Tokens and Custom Claims

Knowing who is signed in, everywhere.

Listeners, tokens and claims

Sign-in state is restored asynchronously when the page loads, so do not read auth.currentUser once at startup; subscribe with onAuthStateChanged and render based on the user it delivers. Each signed-in user has a short-lived ID token (a JWT, refreshed automatically by the SDK) that Firebase services verify; you can send it to your own backend and verify it with the Admin SDK (verifyIdToken). Custom claims are small key-value pairs (such as { admin: true }) set only from trusted code with the Admin SDK; they appear in the token and in rules as request.auth.token. Claims are size-limited (check the docs) and reach the client only after the token refreshes.

Listening for the user and reading claims

Client and Admin SDK, TypeScript.

// client
import { getAuth, onAuthStateChanged } from "firebase/auth";

const auth = getAuth(app);
onAuthStateChanged(auth, async (user) => {
  if (!user) return showSignIn();
  const result = await user.getIdTokenResult(); // pass true to force a refresh
  showApp({ uid: user.uid, isAdmin: result.claims.admin === true });
});

// trusted server code (Cloud Function or your backend)
import { getAuth as getAdminAuth } from "firebase-admin/auth";

export async function makeAdmin(uid: string) {
  await getAdminAuth().setCustomUserClaims(uid, { admin: true });
  // the user sees the claim after their ID token refreshes
}

Claims are for roles, not profiles

Keep claims tiny (roles, tenant id). Store profile data such as names and preferences in Firestore.

Quick check: Where can custom claims be set?

  • In the web config object
  • From any client with updateProfile
  • Inside Security Rules
  • Only from trusted code using the Admin SDK
Answer

Only from trusted code using the Admin SDK — Clients must not grant themselves roles.