Control Access with Custom Claims and Security Rules
Stay organized with collectionsSave and categorize content based on your preferences.
The Firebase Admin SDK supports defining custom attributes on user accounts.
This provides the ability to implement various access control strategies,
including role-based access control, in Firebase apps. These custom attributes
can give users different levels of access (roles), which are enforced
in an application's security rules.
User roles can be defined for the following common cases:
Giving a user administrative privileges to access data and resources.
Defining different groups that a user belongs to.
Providing multi-level access:
Differentiating paid/unpaid subscribers.
Differentiating moderators from regular users.
Teacher/student application, etc.
Add an additional identifier on a user. For example, a Firebase user could map to a different UID in another system.
Let's consider a case where you want to limit access to the database node
"adminContent." You could do that with a database lookup on a list of
admin users. However, you can achieve the same objective more efficiently using
a custom user claim namedadminwith the followingRealtime Databaserule:
Custom user claims are accessible via user's authentication tokens.
In the above example, only users withadminset to true in their token claim
would have read/write
access toadminContentnode. As the ID token already contains these
assertions, no additional processing or lookup is needed to check for admin
permissions. In addition, the ID token is a trusted mechanism for delivering
these custom claims. All authenticated access must validate the ID token before
processing the associated request.
The code examples and solutions described in this page draw from both the
client-side Firebase Auth APIs and the server-side Auth APIs provided by
theAdmin SDK.
Set and validate custom user claims via the Admin SDK
Custom claims can contain sensitive data, therefore they should only be set
from a privileged server environment by the Firebase Admin SDK.
Node.js
// Set admin privilege on the user corresponding to uid.getAuth().setCustomUserClaims(uid,{admin:true}).then(()=>{// The new custom claims will propagate to the user's ID token the// next time a new one is issued.});
Java
// Set admin privilege on the user corresponding to uid.Map<String,Object>claims=newHashMap<>();claims.put("admin",true);FirebaseAuth.getInstance().setCustomUserClaims(uid,claims);// The new custom claims will propagate to the user's ID token the// next time a new one is issued.
Python
# Set admin privilege on the user corresponding to uid.auth.set_custom_user_claims(uid,{'admin':True})# The new custom claims will propagate to the user's ID token the# next time a new one is issued.
Go
// Get an auth client from the firebase.Appclient,err:=app.Auth(ctx)iferr!=nil{log.Fatalf("error getting Auth client: %v\n",err)}// Set admin privilege on the user corresponding to uid.claims:=map[string]interface{}{"admin":true}err=client.SetCustomUserClaims(ctx,uid,claims)iferr!=nil{log.Fatalf("error setting custom claims %v\n",err)}// The new custom claims will propagate to the user's ID token the// next time a new one is issued.
// Set admin privileges on the user corresponding to uid.varclaims=newDictionary<string,object>(){{"admin",true},};awaitFirebaseAuth.DefaultInstance.SetCustomUserClaimsAsync(uid,claims);// The new custom claims will propagate to the user's ID token the// next time a new one is issued.
The custom claims object should not contain anyOIDCreserved key names
orFirebase reserved names. Custom claims payload must not exceed 1000 bytes.
An ID token sent to a backend server can confirm the user's identity and access
level using the Admin SDK as follows:
Node.js
// Verify the ID token first.getAuth().verifyIdToken(idToken).then((claims)=>{if(claims.admin===true){// Allow access to requested admin resource.}});
Java
// Verify the ID token first.FirebaseTokendecoded=FirebaseAuth.getInstance().verifyIdToken(idToken);if(Boolean.TRUE.equals(decoded.getClaims().get("admin"))){// Allow access to requested admin resource.}
Python
# Verify the ID token first.claims=auth.verify_id_token(id_token)ifclaims['admin']isTrue:# Allow access to requested admin resource.pass
Go
// Verify the ID token first.token,err:=client.VerifyIDToken(ctx,idToken)iferr!=nil{log.Fatal(err)}claims:=token.Claimsifadmin,ok:=claims["admin"];ok{ifadmin.(bool){//Allow access to requested admin resource.}}
// Verify the ID token first.FirebaseTokendecoded=awaitFirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);objectisAdmin;if(decoded.Claims.TryGetValue("admin",outisAdmin)){if((bool)isAdmin){// Allow access to requested admin resource.}}
You can also check a user's existing custom claims, which are available as a
property on the user object:
Node.js
// Lookup the user associated with the specified uid.getAuth().getUser(uid).then((userRecord)=>{// The claims can be accessed on the user record.console.log(userRecord.customClaims['admin']);});
Java
// Lookup the user associated with the specified uid.UserRecorduser=FirebaseAuth.getInstance().getUser(uid);System.out.println(user.getCustomClaims().get("admin"));
Python
# Lookup the user associated with the specified uid.user=auth.get_user(uid)# The claims can be accessed on the user record.print(user.custom_claims.get('admin'))
Go
// Lookup the user associated with the specified uid.user,err:=client.GetUser(ctx,uid)iferr!=nil{log.Fatal(err)}// The claims can be accessed on the user record.ifadmin,ok:=user.CustomClaims["admin"];ok{ifadmin.(bool){log.Println(admin)}}
// Lookup the user associated with the specified uid.UserRecorduser=awaitFirebaseAuth.DefaultInstance.GetUserAsync(uid);Console.WriteLine(user.CustomClaims["admin"]);
You can delete a user's custom claims by passing null forcustomClaims.
Propagate custom claims to the client
After new claims are modified on a user via the Admin SDK, they are propagated
to an authenticated user on the client side via the ID token in the following
ways:
A user signs in or re-authenticates after the custom claims are modified. The ID token issued as a result will contain the latest claims.
An existing user session gets its ID token refreshed after an older token expires.
An ID token is force refreshed by callingcurrentUser.getIdToken(true).
Access custom claims on the client
Custom claims can only be retrieved through the user's ID token. Access to these
claims may be necessary to modify the client UI based on the user's role or
access level. However, backend access should always be enforced through the ID
token after validating it and parsing its claims. Custom claims should not be
sent directly to the backend, as they can't be trusted outside of the token.
Once the latest claims have propagated to a user's ID token, you can get them by
retrieving the ID token:
JavaScript
firebase.auth().currentUser.getIdTokenResult().then((idTokenResult)=>{// Confirm the user is an Admin.if(!!idTokenResult.claims.admin){// Show admin UI.showAdminUI();}else{// Show regular user UI.showRegularUI();}}).catch((error)=>{console.log(error);});
user.getIDTokenResult(completion:{(result,error)inguardletadmin=result?.claims?["admin"]as?NSNumberelse{// Show regular user UI.showRegularUI()return}ifadmin.boolValue{// Show admin UI.showAdminUI()}else{// Show regular user UI.showRegularUI()}})
Objective-C
user.getIDTokenResultWithCompletion:^(FIRAuthTokenResult*result,NSError*error){if(error!=nil){BOOL*admin=[result.claims[@"admin"]boolValue];if(admin){// Show admin UI.[selfshowAdminUI];}else{// Show regular user UI.[selfshowRegularUI];}}}];
Best practices for custom claims
Custom claims are only used to provide access control. They are not designed to
store additional data (such as profile and other custom data). While this may
seem like a convenient mechanism to do so, it is strongly discouraged as these
claims are stored in the ID token and could cause performance issues because all
authenticated requests always contain a Firebase ID token corresponding to
the signed in user.
Use custom claims to store data for controlling user access only. All other data should be stored separately via the real-time database or other server side storage.
Custom claims are limited in size. Passing a custom claims payload greater than 1000 bytes will throw an error.
Examples and use cases
The following examples illustrate custom claims in context of specific
Firebase use cases.
Defining roles via Firebase Functions on user creation
In this example, custom claims are set on a user on creation usingCloud Functions.
Custom claims can be added usingCloud Functions, and propagated immediately
withRealtime Database. The function is called only on signup using anonCreatetrigger. Once the custom claims are set, they propagate to all existing and
future sessions. The next time the user signs in with the user credential,
the token contains the custom claims.
The same flow can be used when upgrading an existing user's access level.
Take for example a free user upgrading to a paid subscription. The user's ID
token is sent with the payment information to the backend server via an HTTP
request. When the payment is successfully processed, the user is set as a paid
subscriber via the Admin SDK. A successful HTTP response is returned to the
client to force token refresh.
Defining roles via backend script
A recurring script (not initiated from the client) could be set to run to
update user custom claims:
Node.js
getAuth().getUserByEmail('user@admin.example.com').then((user)=>{// Confirm user is verified.if(user.emailVerified){// Add custom claims for additional privileges.// This will be picked up by the user on token refresh or next sign in on new device.returngetAuth().setCustomUserClaims(user.uid,{admin:true,});}}).catch((error)=>{console.log(error);});
Java
UserRecorduser=FirebaseAuth.getInstance().getUserByEmail("user@admin.example.com");// Confirm user is verified.if(user.isEmailVerified()){Map<String,Object>claims=newHashMap<>();claims.put("admin",true);FirebaseAuth.getInstance().setCustomUserClaims(user.getUid(),claims);}
Python
user=auth.get_user_by_email('user@admin.example.com')# Confirm user is verifiedifuser.email_verified:# Add custom claims for additional privileges.# This will be picked up by the user on token refresh or next sign in on new device.auth.set_custom_user_claims(user.uid,{'admin':True})
Go
user,err:=client.GetUserByEmail(ctx,"user@admin.example.com")iferr!=nil{log.Fatal(err)}// Confirm user is verifiedifuser.EmailVerified{// Add custom claims for additional privileges.// This will be picked up by the user on token refresh or next sign in on new device.err:=client.SetCustomUserClaims(ctx,user.UID,map[string]interface{}{"admin":true})iferr!=nil{log.Fatalf("error setting custom claims %v\n",err)}}
UserRecorduser=awaitFirebaseAuth.DefaultInstance.GetUserByEmailAsync("user@admin.example.com");// Confirm user is verified.if(user.EmailVerified){varclaims=newDictionary<string,object>(){{"admin",true},};awaitFirebaseAuth.DefaultInstance.SetCustomUserClaimsAsync(user.Uid,claims);}
Custom claims can also be modified incrementally via the Admin SDK:
Node.js
getAuth().getUserByEmail('user@admin.example.com').then((user)=>{// Add incremental custom claim without overwriting existing claims.constcurrentCustomClaims=user.customClaims;if(currentCustomClaims['admin']){// Add level.currentCustomClaims['accessLevel']=10;// Add custom claims for additional privileges.returngetAuth().setCustomUserClaims(user.uid,currentCustomClaims);}}).catch((error)=>{console.log(error);});
Java
UserRecorduser=FirebaseAuth.getInstance().getUserByEmail("user@admin.example.com");// Add incremental custom claim without overwriting the existing claims.Map<String,Object>currentClaims=user.getCustomClaims();if(Boolean.TRUE.equals(currentClaims.get("admin"))){// Add level.currentClaims.put("level",10);// Add custom claims for additional privileges.FirebaseAuth.getInstance().setCustomUserClaims(user.getUid(),currentClaims);}
Python
user=auth.get_user_by_email('user@admin.example.com')# Add incremental custom claim without overwriting existing claims.current_custom_claims=user.custom_claimsifcurrent_custom_claims.get('admin'):# Add level.current_custom_claims['accessLevel']=10# Add custom claims for additional privileges.auth.set_custom_user_claims(user.uid,current_custom_claims)
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-09-04 UTC."],[],[],null,[]]