iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Custom Claims

Custom claims are key/value pairs you attach to a Firebase Auth user. They live in the ID token, available to client SDKs and Firestore/Storage security rules without an extra round-trip.

Set, refresh, use in rules

EXAMPLE
// 1) Set claims server-side (Admin SDK, Node.js)
import { getAuth } from 'firebase-admin/auth';
const auth = getAuth();

await auth.setCustomUserClaims(uid, {
    role:    'admin',
    org:     'org_42',
    plan:    'pro',
});

// 2) Read claims server-side
const user = await auth.getUser(uid);
console.log(user.customClaims);

// 3) Force the client to refresh — claims only update on next token refresh
// Client-side after server tells you claims changed:
import { getAuth as getClientAuth } from 'firebase/auth';
const current = getClientAuth().currentUser;
await current.getIdToken(true);   // force refresh — picks up new claims

// 4) Read claims on the client
const { claims } = await current.getIdTokenResult();
console.log(claims.role);     // 'admin'

// 5) Use in Firestore security rules
// firestore.rules
rules_version = '2';
service cloud.firestore {
    match /databases/{db}/documents {
        match /orgs/{org}/posts/{post} {
            allow read:  if request.auth != null
                          && request.auth.token.org == org;
            allow write: if request.auth.token.role == 'admin'
                          && request.auth.token.org == org;
        }
        match /admin/{doc=**} {
            allow read, write: if request.auth.token.role == 'admin';
        }
    }
}

// 6) Storage rules
rules_version = '2';
service firebase.storage {
    match /b/{bucket}/o {
        match /orgs/{org}/{allPaths=**} {
            allow read, write: if request.auth != null
                                && request.auth.token.org == org;
        }
    }
}

// 7) Cloud Function — set claims on signup
import { onCreate } from 'firebase-functions/v2/auth';
export const initClaims = onCreate(async (user) => {
    const role = user.email?.endsWith('@example.com') ? 'admin' : 'user';
    await auth.setCustomUserClaims(user.uid, { role, org: 'org_default' });
});

// 8) Limits + best practices
//   • Max 1000 bytes per user across all claims
//   • Only set what your rules need — NOT user profile data
//   • Claims propagate after the user refreshes their token (default: ~1 hour)
//   • Always validate server-side too — claims live in the token, can be stale

Why it matters

Custom claims are the right place for “is this user an admin / what org are they in.” They’re stamped into the JWT so rules can authorise without an extra Firestore lookup — massively cheaper at scale.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// admin SDK only (Cloud Function)
await admin.auth().setCustomUserClaims(uid, { admin: true });
// Client checks via token
const token = await user.getIdTokenResult();
if (token.claims.admin) { /* … */ }
Try it Yourself »

Discussion

Loading…