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…