This error occurs when a Firebase Callable Function rejects a request due to missing, invalid, or expired authentication credentials. The client must be signed in with Firebase Authentication, and the ID token must be valid and properly included in the request headers.
The UNAUTHENTICATED error in Firebase Callable Functions indicates that the request to your cloud function failed authentication validation. Firebase callable functions automatically handle authentication by extracting and validating the user's ID token from the request. If the token is missing, expired, invalid, or cannot be verified, the request is rejected immediately with a 401 Unauthorized response. This is a security feature that prevents unauthenticated requests from reaching your function code. The "Invalid credentials" variant specifically means Firebase was able to detect and parse the authentication token, but the token itself failed validation - either because it was expired, revoked, tampered with, or the user's account is no longer valid. Unlike regular HTTP functions where you manually check authentication, callable functions enforce this automatically. The Firebase client SDKs are responsible for obtaining a valid ID token from Firebase Authentication and including it in every callable function request.
The most common cause of this error is calling a callable function while the user is not authenticated. Always check that a user is signed in before invoking the function.
// Web SDK
import { getAuth, onAuthStateChanged } from 'firebase/auth';
import { getFunctions, httpsCallable } from 'firebase/functions';
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, safe to call function
const functions = getFunctions();
const myFunction = httpsCallable(functions, 'myFunctionName');
myFunction({ data: 'value' }).then(result => {
console.log('Success:', result.data);
}).catch(error => {
console.error('Function error:', error.code, error.message);
});
} else {
console.log('User is not signed in');
}
});// Android
import com.google.firebase.auth.ktx.auth
import com.google.firebase.functions.ktx.functions
import com.google.firebase.ktx.Firebase
val auth = Firebase.auth
val currentUser = auth.currentUser
if (currentUser != null) {
val functions = Firebase.functions
functions.getHttpsCallable("myFunctionName")
.call(mapOf("data" to "value"))
.addOnSuccessListener { result ->
Log.d("Function", "Success: ${result.data}")
}
.addOnFailureListener { exception ->
Log.e("Function", "Error: ${exception.message}")
}
} else {
Log.d("Auth", "User is not signed in")
}// iOS/Swift
import FirebaseAuth
import FirebaseFunctions
let auth = Auth.auth()
if let user = auth.currentUser {
let functions = Functions.functions()
functions.httpsCallable("myFunctionName").call(["data": "value"]) { result, error in
if let error = error {
print("Error: \(error.localizedDescription)")
} else {
print("Success: \(result?.data ?? "")")
}
}
} else {
print("User is not signed in")
}Wait for the authentication state to be determined before calling any callable functions. Use the SDK's auth state listener rather than checking synchronously, as auth initialization is asynchronous.
If no user is signed in, authenticate first using Firebase Authentication methods before calling callable functions. All callable functions require an authenticated user.
// Email/password sign-in
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';
import { getFunctions, httpsCallable } from 'firebase/functions';
const auth = getAuth();
signInWithEmailAndPassword(auth, '[email protected]', 'password')
.then((userCredential) => {
// User is now signed in, can call functions
const functions = getFunctions();
const myFunction = httpsCallable(functions, 'myFunctionName');
return myFunction({ userId: userCredential.user.uid });
})
.catch((error) => {
console.error('Sign-in error:', error.code);
});// Anonymous sign-in
import { getAuth, signInAnonymously } from 'firebase/auth';
const auth = getAuth();
signInAnonymously(auth)
.then((userCredential) => {
console.log('Signed in anonymously as:', userCredential.user.uid);
// Can now call functions
})
.catch((error) => {
console.error('Error:', error.code);
});// Google Sign-in
import { getAuth, signInWithPopup, GoogleAuthProvider } from 'firebase/auth';
const auth = getAuth();
const provider = new GoogleAuthProvider();
signInWithPopup(auth, provider)
.then((result) => {
// User is signed in with Google
})
.catch((error) => {
console.error('Error:', error.code);
});Choose the authentication method appropriate for your application (email, social login, anonymous, etc.).
The Firebase client SDKs automatically include the user's ID token in callable function requests. Verify you are using the official SDK's httpsCallable function, not making raw HTTP requests.
// Correct - SDK automatically includes token
import { getFunctions, httpsCallable } from 'firebase/functions';
const functions = getFunctions();
const myFunction = httpsCallable(functions, 'myFunctionName');
await myFunction({ /* data */ }); // Token included automatically// Incorrect - manual HTTP request (token not included)
const response = await fetch('https://us-central1-myproject.cloudfunctions.net/myFunction', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ /* data */ })
// Token is NOT automatically included
});If you need to make raw HTTP requests, you must manually obtain the ID token and include it:
import { getAuth } from 'firebase/auth';
const auth = getAuth();
const token = await auth.currentUser.getIdToken();
const response = await fetch('https://us-central1-myproject.cloudfunctions.net/myFunction', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token // Must include token manually
},
body: JSON.stringify({ data: { /* your data */ } })
});Always use the official SDK's httpsCallable when possible, as it handles token management automatically.
ID tokens expire after one hour. If a user has been idle or the token may have expired, force a refresh before calling the function.
import { getAuth } from 'firebase/auth';
import { getFunctions, httpsCallable } from 'firebase/functions';
const auth = getAuth();
// Force token refresh
await auth.currentUser.getIdToken(true);
// Now call the function with fresh token
const functions = getFunctions();
const myFunction = httpsCallable(functions, 'myFunctionName');
const result = await myFunction({ userId: auth.currentUser.uid });// Android
val user = Firebase.auth.currentUser
user?.getIdToken(true)?.addOnSuccessListener { result ->
val token = result.token
// Token is now refreshed, can call function
val functions = Firebase.functions
functions.getHttpsCallable("myFunctionName").call(mapOf())
}// iOS
Auth.auth().currentUser?.getIDTokenForcingRefresh(true) { idToken, error in
if let error = error {
print("Error refreshing token: \(error.localizedDescription)")
return
}
// Token is refreshed, can now call function
let functions = Functions.functions()
functions.httpsCallable("myFunctionName").call(nil)
}The SDK typically handles this automatically, but forcing a refresh can resolve issues with expired tokens.
Ensure your client app and cloud function are configured for the same Firebase project. Mismatched configurations will cause authentication failures.
// Client - check configuration
import { initializeApp } from 'firebase/app';
const firebaseConfig = {
apiKey: 'YOUR_API_KEY',
projectId: 'YOUR_PROJECT_ID',
appId: 'YOUR_APP_ID'
// ... other config
};
const app = initializeApp(firebaseConfig);
console.log('Configured project:', firebaseConfig.projectId);// Server - check configuration in firebase.json
{
"functions": {
"source": "functions",
"codebase": "default",
"runtime": "nodejs18"
},
"firebaserc": ".firebaserc"
}Check that:
- The API Key is for the correct Firebase project
- The Project ID in client config matches your Firebase project
- Cloud Functions are deployed to the same project
- No proxy or alternative endpoints are being used
You can verify the current project with:
firebase projects:list
firebase use --list # Shows currently selected projectVerify that Firebase Authentication is enabled in your Firebase Console and that your project allows sign-ups for your authentication method.
In Firebase Console:
1. Go to Authentication section
2. Ensure the service shows "Enabled"
3. Check the "Sign-in method" tab
4. Enable at least one authentication provider (Email/Password, Google, Anonymous, etc.)
# Verify via Firebase CLI
firebase auth:import users.json # This will tell you if auth is enabled
# Or check project settings
firebase projects:listIf Authentication is disabled, enable it in the Console before attempting to use callable functions.
Token Expiration: Firebase ID tokens expire after 1 hour. The client SDK automatically refreshes tokens before expiry, but network issues or clock skew can cause problems. The token includes timestamps (iat, exp) that the server verifies. If your device clock is significantly off, token validation will fail.
ID Token Structure: The ID token is a JWT containing user information, custom claims, and expiration time. It's signed by Firebase and verified by the function trigger. Tampering with the token or modifying claims will cause validation to fail.
Anonymous Authentication: Anonymous users get valid ID tokens and can call callable functions. If you want to restrict functions to only authenticated users, check for anonymous users server-side: if (!request.auth || request.auth.token.firebase.sign_in_provider === 'anonymous') throw new Error(...)
Custom Claims: If you set custom claims on users (e.g., admin roles), these are included in the ID token. However, custom claims are cached for up to an hour. Changes to custom claims won't appear in the token immediately - the user may need to re-authenticate or you can refresh the token explicitly.
App Check Integration: If you enable Firebase App Check on your functions, requests must include a valid App Check token in addition to the authentication token. A request can fail due to invalid App Check even if authentication is valid.
Emulator Issues: When using the Firebase Local Emulator Suite, ensure your client is configured to connect to the emulator. Use connectAuthEmulator(auth, 'http://localhost:9099') and connectFunctionsEmulator(functions, 'localhost', 5001). Mismatched emulator configuration is a common cause of authentication errors.
Revoked Sessions: If a user's password is changed, multi-factor authentication is modified, or the account is disabled, their current ID tokens are invalidated. The client will need to re-authenticate.
Callable Functions: INTERNAL - Unhandled exception
How to fix "Callable Functions: INTERNAL - Unhandled exception" in Firebase
auth/invalid-hash-algorithm: Hash algorithm doesn't match supported options
How to fix "auth/invalid-hash-algorithm: Hash algorithm doesn't match supported options" in Firebase
Hosting: CORS configuration not set up properly
How to fix CORS configuration in Firebase Hosting
auth/reserved-claims: Custom claims use reserved OIDC claim names
How to fix "reserved claims" error when setting custom claims in Firebase
messaging/message-rate-exceeded: Overall sending rate too high
Overall sending rate too high in Firebase Cloud Messaging