Skip to content

Firebase Auth Custom Claims — Role-Based Authorization with Security Rules

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Firebase Auth Custom Claims. We cover key concepts, practical examples, and best practices to help you master this topic.

Firebase Auth custom claims are key-value pairs attached to user accounts via the Admin SDK, enabling role-based authorization checked in Firestore security rules and client-side code.

What You'll Learn

  • Setting custom claims with the Admin SDK
  • Reading claims in Firestore security rules
  • Best practices for role management

Why It Matters

Custom claims enable server-authoritative role-based access control. Unlike client-side role checks, claims set via Admin SDK cannot be tampered with. DodaTech uses custom claims for admin, moderator, and premium user roles.

flowchart TD
    A["Admin SDK sets claims"] --> B["Claims stored on user account"]
    B --> C["User authenticates"]
    C --> D["ID token includes claims"]
    D --> E["Security rules check claims"]
    D --> F["Client reads claims"]
    E -->|"Admin claim present"| G["Allow write access"]
    E -->|"No admin claim"| H["Deny write access"]

Code Examples

// Admin SDK: Set custom claims (Node.js)
const admin = require('firebase-admin');

async function setUserRole(uid, role) {
  try {
    // Set claims
    await admin.auth().setCustomUserClaims(uid, {
      role: role,
      admin: role === 'admin',
      premium: ['premium', 'admin'].includes(role)
    });

    console.log(`Set role ${role} for ${uid}`);
  } catch (error) {
    console.error('Error setting claims:', error);
  }
}

// Example usage
await setUserRole('user123', 'admin');
await setUserRole('user456', 'premium');
await setUserRole('user789', 'user');
// Firestore security rules using custom claims
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    function isAdmin() {
      return request.auth != null
        && request.auth.token.admin == true;
    }

    function hasRole(role) {
      return request.auth != null
        && request.auth.token.role == role;
    }

    function isPremium() {
      return request.auth != null
        && request.auth.token.premium == true;
    }

    match /users/{userId} {
      allow read: if request.auth != null;
      allow write: if isAdmin()
        || request.auth.uid == userId;
    }

    match /admin/{document} {
      allow read, write: if isAdmin();
    }

    match /premium/{document} {
      allow read: if isPremium();
      allow write: if isAdmin();
    }
  }
}
// Client-side: Read custom claims
import { auth } from './firebase';
import { getIdTokenResult } from 'firebase/auth';

async function checkUserRole() {
  const user = auth.currentUser;
  if (!user) {
    console.log('Not logged in');
    return null;
  }

  // Get the ID token with claims
  const idTokenResult = await getIdTokenResult(user);
  console.log('Claims:', idTokenResult.claims);

  if (idTokenResult.claims.admin) {
    console.log('User is admin');
    return 'admin';
  }
  if (idTokenResult.claims.premium) {
    console.log('User is premium');
    return 'premium';
  }
  return 'user';
}

// React example
function UserDashboard() {
  const [role, setRole] = useState(null);

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged(async (user) => {
      if (user) {
        const result = await getIdTokenResult(user);
        setRole(result.claims.role);
      }
    });
    return unsubscribe;
  }, []);

  if (role === 'admin') return <AdminPanel />;
  if (role === 'premium') return <PremiumPanel />;
  return <BasicPanel />;
}
# Python Admin SDK: Set and verify claims
import firebase_admin
from firebase_admin import auth

# Set claims
auth.set_custom_user_claims('user123', {
    'role': 'admin',
    'admin': True,
    'department': 'engineering'
})

# Get user claims
user = auth.get_user('user123')
print(user.custom_claims)

# Verify claims on the server
def verify_admin(id_token):
    decoded_token = auth.verify_id_token(id_token)
    if decoded_token.get('admin'):
        return True
    raise PermissionError('Admin access required')

Common Mistakes

1. Storing Large Data in Custom Claims

Claims are limited to 1000 bytes. Store only role identifiers, not user data.

2. Not Caching Claims on the Client

Calling getIdTokenResult on every render is expensive. Cache the result in state.

3. Setting Claims Without Verification

Always verify user identity before setting admin or elevated claims.

4. Forgetting to Refresh Tokens

Claims changes take effect on next token refresh. Force refresh with getIdToken(true).

5. Using Claims for User Profile Data

Claims are for authorization. Store profile data in Firestore.

Practice Questions

  1. How do you set custom claims on a user?
  2. What is the maximum size of custom claims?
  3. How do you read claims in Firestore security rules?
  4. How do you read claims on the client side?
  5. How do you remove custom claims?

Answers:

  1. Use admin.auth().setCustomUserClaims(uid, claims).
  2. 1000 bytes.
  3. Access request.auth.token.claimName in security rules.
  4. Call getIdTokenResult(user) and access the claims property.
  5. Call setCustomUserClaims(uid, null) to remove all claims.

Challenge: Build a role management system with custom claims. Create an admin dashboard to assign roles (user, premium, moderator, admin), set claims via Admin SDK, enforce access in Firestore security rules, and display role-appropriate UI on the client.

FAQ

How quickly do custom claims propagate?

Claim changes are reflected on the next token refresh. Force immediate refresh with getIdToken(true).

Can users modify their own custom claims?

No. Custom claims can only be set via the Admin SDK, which requires privileged credentials. Client-side code cannot modify claims.

How many custom claims can I set?

The total claims payload must not exceed 1000 bytes. There is no specific limit on the number of claims.

Can I use custom claims with OAuth providers?

Yes. Custom claims work with all authentication methods. They are attached to the user account regardless of how the user signed in.

How do I audit claim changes?

Implement logging in your Admin SDK endpoint that sets claims. Record who changed what claim and when.

Mini Project

Build a complete role-based access control system: Admin SDK endpoint for setting claims, Firestore security rules enforcing read/write permissions based on roles, client UI adapting to user role, and an admin dashboard for managing user roles with audit logging.

What's Next

Learn about the Firebase Admin SDK for advanced server-side operations, then explore Firebase Storage for file uploads.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro