How to fix auth/reserved-claims: Custom claims use reserved OIDC claim names in Firebase

FirebaseINTERMEDIATEHIGHUpdated Dec 29, 2025· 114 views

Firebase custom claims cannot use OIDC reserved names like "iss", "sub", "aud", "exp", "iat", or Firebase reserved names. This error occurs when you try to set custom user claims with these reserved keywords.

Quick fix

Fixes auth/reserved-claims

const admin = require('firebase-admin');

admin.auth().setCustomUserClaims(uid, {
  role: 'admin',
  aud: 'my-app'  // ❌ 'aud' is a reserved OIDC claim
})
.catch(error => console.log(error.message)); // auth/reserved-claims
  1. Review the exact reserved claim name from the error
  2. Remove the reserved claim from your custom claims object
  3. Rename the claim if you need similar functionality
  4. Verify your custom claims size limit
firebase — error
const admin = require('firebase-admin');
admin.auth().setCustomUserClaims(uid, {
role: 'admin',
aud: 'my-app' // ❌ 'aud' is a reserved OIDC claim
})
.catch(error => console.log(error.message)); // auth/reserved-claims
auth/reserved-claims: Custom claims use reserved OIDC claim names

What this error means

Firebase Authentication tokens comply with the OpenID Connect (OIDC) JWT specification, which reserves certain claim names for standard use. Additionally, Firebase reserves its own claim names for internal functionality. When you attempt to set custom user claims using any of these reserved names, Firebase rejects the operation with this error to prevent token conflicts and security issues. The reserved OIDC claims include identity claims like "iss" (issuer), "sub" (subject), and "aud" (audience), as well as temporal claims like "exp" (expiration), "iat" (issued at), and "auth_time". Using these names in custom claims would overwrite critical token metadata, breaking authentication and authorization logic.

Typical symptoms

  • Firebase Admin SDK rejects setCustomUserClaims() call with 'auth/reserved-claims' error
  • Custom claims are not applied to the user's token
  • Application fails during user setup or role assignment
  • Error message specifies which claim name is reserved

Common causes

  1. Using OIDC standard claim names (iss, sub, aud, exp, iat, auth_time, nonce, acr, amr, azp)
  2. Using Firebase reserved claim names (such as firebase, user_id, or other system-level claims)
  3. Accidentally including standard identity claims in custom claims object (name, given_name, family_name, picture, etc.)
  4. Copy-pasting custom claims that were designed for a different auth system

How to fix "auth/reserved-claims: Custom claims use reserved OIDC claim names"

1Review the exact reserved claim name from the error

The error message will specify which claim name is reserved. Note the exact name, as you'll need to either remove or rename it.

Common reserved OIDC claims:
- iss (issuer)
- sub (subject)
- aud (audience)
- exp (expiration)
- iat (issued at)
- auth_time (authentication time)
- nonce (prevents replay attacks)
- acr (authentication context)
- amr (authentication methods)
- azp (authorized party)

2Remove the reserved claim from your custom claims object

If the claim is not essential to your application logic, simply delete it from the object you're passing to setCustomUserClaims().

Example - WRONG:

typescript
const admin = require('firebase-admin');

admin.auth().setCustomUserClaims(uid, {
  role: 'admin',
  aud: 'my-app'  // ❌ 'aud' is a reserved OIDC claim
})
.catch(error => console.log(error.message)); // auth/reserved-claims

Example - CORRECT:

typescript
const admin = require('firebase-admin');

admin.auth().setCustomUserClaims(uid, {
  role: 'admin',
  // ✅ 'aud' removed - audience is handled by Firebase
})
.then(() => console.log('Custom claims set'))
.catch(error => console.log(error));
3Rename the claim if you need similar functionality

If you need to store data similar to a reserved claim, use a custom prefix or different naming convention.

Example - Use a custom namespace:

typescript
const admin = require('firebase-admin');

// Instead of 'iat' (issued at), use 'assigned_at'
// Instead of 'exp' (expires), use 'token_expires_at'

admin.auth().setCustomUserClaims(uid, {
  role: 'admin',
  permissions: ['read', 'write', 'delete'],
  assigned_at: new Date().toISOString(),
  token_expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
})
.then(() => console.log('Custom claims set'))
.catch(error => console.log(error));

This approach lets you store similar data without conflicting with reserved OIDC claims.

4Verify your custom claims size limit

While fixing the reserved names, ensure your entire custom claims payload doesn't exceed 1000 bytes. Firebase enforces this strict size limit.

typescript
const admin = require('firebase-admin');

const customClaims = {
  role: 'admin',
  permissions: ['read', 'write', 'delete'],
  department: 'engineering',
  level: 5,
};

// Check size (rough estimate - Firebase uses exact JSON serialization)
const size = JSON.stringify(customClaims).length;
console.log(`Claims size: ${size} bytes`); // Should be < 1000

if (size > 1000) {
  console.warn('Custom claims exceed 1000 bytes!');
} else {
  admin.auth().setCustomUserClaims(uid, customClaims)
    .then(() => console.log('Custom claims set successfully'))
    .catch(error => console.error('Error:', error));
}
5Force token refresh after updating claims

After successfully setting custom claims, the user's existing tokens won't be updated automatically. You need to force a token refresh on the client side.

typescript
// Client-side (Web SDK)
import { getAuth } from 'firebase/auth';

const auth = getAuth();
const currentUser = auth.currentUser;

if (currentUser) {
  // Force refresh to get updated claims in token
  currentUser.getIdToken(true)
    .then(idToken => {
      console.log('Token refreshed with new claims');
      // Use the new token for API calls
    })
    .catch(error => console.error('Failed to refresh token:', error));
}
swift
// iOS
let user = Auth.auth().currentUser
user?.getIDTokenForcingRefresh(true) { idToken, error in
  if let error = error {
    print("Error refreshing token:", error)
    return
  }
  print("Token refreshed with new claims")
}
java
// Android
FirebaseAuth mAuth = FirebaseAuth.getInstance();
FirebaseUser user = mAuth.getCurrentUser();

user.getIdToken(true)
  .addOnCompleteListener(task -> {
    if (task.isSuccessful()) {
      String idToken = task.getResult().getToken();
      Log.d("Auth", "Token refreshed with new claims");
    } else {
      Log.e("Auth", "Error refreshing token", task.getException());
    }
  });

Advanced notes

Custom claims should only be used for access control (role-based or attribute-based authorization), not for storing user data. Storing large amounts of data in custom claims increases your ID token size and can impact performance since tokens are sent with every authenticated request.

For role-based access control (RBAC), store role definitions in Firestore and only use custom claims for simple role identifiers (e.g., role: 'admin'). For attribute-based access control (ABAC), consider using a separate database table or Firestore collection to store user permissions, then query it at request time rather than embedding everything in custom claims.

The 1000-byte limit is a hard Firebase limit across all SDKs and platforms. Plan your claims structure accordingly, removing unnecessary fields and using short key names if needed.

Frequently asked questions

What does "auth/reserved-claims" mean?
Firebase Authentication tokens comply with the OpenID Connect (OIDC) JWT specification, which reserves certain claim names for standard use. Additionally, Firebase reserves its own claim names for internal functionality. When you attempt to set custom user claims using any of these reserved names, Firebase rejects the operation with this error to prevent token conflicts and security issues.
How do I fix "auth/reserved-claims"?
Firebase custom claims cannot use OIDC reserved names like "iss", "sub", "aud", "exp", "iat", or Firebase reserved names. This error occurs when you try to set custom user claims with these reserved keywords.
What causes "auth/reserved-claims"?
Using OIDC standard claim names (iss, sub, aud, exp, iat, auth_time, nonce, acr, amr, azp) Using Firebase reserved claim names (such as firebase, user_id, or other system-level claims) Accidentally including standard identity claims in custom claims object (name, given_name, family_name, picture, etc.)
Is the "auth/reserved-claims" error serious?
Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.

Official resources & further reading