Firebase Security Rules with Custom Claims: One Project, Four Access Levels
VietProHub runs public corporate sites and private family tools in a single Firebase project. One Firestore database holds blog comments, family photos, kids progress, and admin audit logs. The question is always the same: who can touch what?
The wrong approach: client-side checks only
Early versions relied on client-side guards. Wrong twice over: anyone can bypass the UI, and Firestore rules that merely check request.auth != null let any signed-in stranger read every document.
Custom claims: the key primitive
Firebase Auth custom claims are a small JSON object attached to a user's ID token:
{ "admin": true, "dashboard_access": true }
Claims live in the token, so security rules can read them directly — no database lookup, no extra round trip:
function hasFamilyAccess() {
return request.auth != null
&& request.auth.token.family_access == true;
}
Claims are set server-side, either from the admin dashboard (via our API gateway) or through admin.auth().setCustomUserClaims().
Per-document ownership on top
Claims gate the app. Ownership rules gate the document:
match /familyMembers/{memberId} {
allow read: if hasFamilyAccess();
allow write: if hasFamilyAccess() && request.auth.uid == resource.data.ownerId;
}
Blog comments stay public-readable but only their author can edit. Kids progress is writeable only by the parent role. Audit logs are admin-only, no exceptions.
Testability is the hidden win
Rules are just code. We run them against emulators with scripted scenarios — guest reads a photo (denied), family member writes an album (allowed), a stranger claims admin (impossible, claims are signed). The rules file is the security review artifact; changes get reviewed like code, because they are.
What we'd do differently
Claims are cached in the ID token for up to an hour. After a role change, users see stale permissions until token refresh. In practice we force-refresh on login and after role edits — acceptable, but worth knowing before you build role management on top of claims.