This error occurs when Firebase App Check cannot verify the authenticity of requests to your Firebase services. Token validation failures can stem from misconfigured providers, server time mismatches, expired tokens, or incorrect app registration.
The "App Check: Token Validation Failed" error indicates that Firebase App Check is unable to authenticate and verify that requests are coming from a legitimate instance of your app. This security check validates that requests originate from your app running on a registered device with proper attestation from the platform provider (Play Integrity, SafetyNet, DeviceCheck, or reCAPTCHA). App Check works by having your client app generate a token from its attestation provider before making requests. Your backend then verifies this token is legitimate by checking its signature, expiration, and other claims. When verification fails, it means either the token is malformed, the app isn't properly registered, or the attestation provider couldn't verify the app's authenticity. This is a critical error because it blocks legitimate app access to your Firebase resources as a security measure.
First, ensure your app is properly registered with the correct identifiers:
1. Open [Firebase Console](https://console.firebase.google.com/)
2. Navigate to your project → Project Settings
3. Verify your app is listed with the correct:
- Package name (Android): Exact match, case-sensitive
- Bundle ID (iOS): Exact match, case-sensitive
- App ID (Web): Present and accessible
4. For Android, verify certificate fingerprints:
# Get SHA-1 fingerprint
./gradlew signingReport
# Or using keytool
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android5. Compare the output with what's registered in Firebase Console → App Check tab
If the app doesn't appear in Firebase Console, add it:
- Click "Add app" in Project Settings
- Select your platform (Android/iOS/Web)
- Enter the exact package name/bundle ID
- For Android: Add both SHA-1 and SHA-256 fingerprints
App Check uses platform-specific attestation providers. Ensure the right one is configured:
For Android:
- Recommended: Play Integrity API (modern replacement for SafetyNet)
- Requires linking Firebase to Google Play Console
- Provides robust device verification
- Alternative: SafetyNet Attestation API (deprecated but still works)
For iOS:
- Recommended: App Attest (iOS 14.0+)
- Hardware-based attestation with no server calls needed
- Alternative: DeviceCheck (iOS 11.0+)
- Server-based device verification
For Web:
- Only option: reCAPTCHA v3
- Requires reCAPTCHA v3 API key configured
To configure in Firebase Console:
1. Go to App Check tab
2. Click "Manage" on your app
3. Select the appropriate provider
4. Follow provider-specific setup instructions
Example Web setup:
import { initializeAppCheck, ReCaptchaV3Provider } from "firebase/app-check";
initializeAppCheck(app, {
provider: new ReCaptchaV3Provider("YOUR_RECAPTCHA_V3_PUBLIC_KEY"),
isTokenAutoRefreshEnabled: true
});Example Android setup:
val playIntegrityProvider = PlayIntegrityAppCheckProviderFactory.getInstance()
FirebaseAppCheck.getInstance().installAppCheckProviderFactory(playIntegrityProvider)Development builds often can't generate valid production tokens. Use debug tokens instead:
1. In Firebase Console → App Check → Manage App:
- Enable "App Check Debug Provider"
- Copy the debug token
2. Register the debug token in your app:
Web:
// Call BEFORE initializing Firebase
self.FIREBASE_APPCHECK_DEBUG_TOKEN = "YOUR_DEBUG_TOKEN_HERE";
// Or set it dynamically
if (process.env.NODE_ENV === 'development') {
self.FIREBASE_APPCHECK_DEBUG_TOKEN = "YOUR_DEBUG_TOKEN";
}Android:
// In onCreate of your Application class
FirebaseAppCheck.getInstance().installAppCheckProviderFactory(
DebugAppCheckProviderFactory()
)iOS:
#if DEBUG
let debugProvider = AppCheckDebugProviderFactory()
AppCheck.setAppCheckProvider(debugProvider)
#else
// Use production provider
let deviceCheckProvider = DeviceCheckProvider()
AppCheck.setAppCheckProvider(deviceCheckProvider)
#endifImportant: Debug tokens only work during development. Remove debug provider code before deploying to production.
Token validation includes timestamp checks. Clock skew causes "token not yet valid" errors:
Check your device time:
# On Android device
adb shell date
# On iOS, check Settings → General → Date & Time
# Ensure "Set Automatically" is enabledCheck your backend server time:
# On your server/Cloud Function
date -u
timedatectl status # On LinuxIf times differ by more than a few seconds:
For development devices:
- Ensure NTP time sync is enabled
- Manually set correct time
For servers:
- Check NTP daemon is running
- Verify timezone is correct
- For Cloud Functions: Usually auto-synced, but check region
For testing:
- Temporarily disable token validation in development
- Use Firebase Rules with request.firebaseAppCheck.token.claims.app_id to debug
Example with detailed error logging:
try {
const decodedToken = await admin.appCheck().verifyIdToken(appCheckToken);
console.log("Token verified:", decodedToken);
} catch (error) {
console.error("Token verification failed:", error.message);
console.error("Error code:", error.code);
// "The token is not yet valid (iat)" indicates clock skew
}Ensure tokens are properly generated and sent to your backend:
Client-side token generation:
Web:
import { getAppCheck, getToken } from "firebase/app-check";
const appCheck = getAppCheck();
const { token } = await getToken(appCheck, /* forceRefresh */ true);
// token is the complete JWT stringAndroid:
val appCheck = FirebaseAppCheck.getInstance()
appCheck.getAppCheckToken(false)
.addOnSuccessListener { result ->
val token = result.token
// Use this complete token
}Sending to backend:
- Send the ENTIRE token string (not decoded)
- Include in Authorization header or custom header
- Don't truncate or modify the token
fetch('/api/protected', {
headers: {
'X-Firebase-AppCheck': token // Send complete token
}
})Backend verification:
// Node.js with Firebase Admin SDK
const appCheckToken = req.headers['x-firebase-appcheck'];
try {
const decodedToken = await admin.appCheck().verifyIdToken(appCheckToken);
// Token is valid, proceed with request
} catch (error) {
console.error('Invalid token:', error.message);
res.status(401).send('App Check token validation failed');
}Common mistakes:
- Sending only part of the token
- URL-encoding or base64-encoding the token
- Forgetting to pass the entire string
Configuration changes in Firebase Console take time to propagate globally:
After any of these actions, wait 15-30 minutes before testing:
- Adding a new app
- Registering certificate fingerprints
- Enabling App Check enforcement
- Changing attestation providers
- Updating provider configuration
During this window:
- Some regions may have old configuration
- Token validation may be inconsistent
- Errors like "app not found" may occur
To verify propagation is complete:
1. Test from multiple locations/networks
2. Check Firebase Console → App Check → Usage metrics
3. Monitor Cloud Functions logs for patterns in validation failures
If errors persist after 30 minutes:
- Try disabling and re-enabling App Check
- Delete and re-add your app in Firebase
- Clear app cache on test devices
# Clear Android app cache
adb shell pm clear com.example.myapp
# Clear iOS app cache
# Settings → General → iPhone Storage → [App] → Offload App (then reinstall)Backend token verification fetches the public key set from Google's JWKS endpoint:
The endpoint is: https://firebaseappcheck.googleapis.com/v1/jwks
Test endpoint accessibility:
# From your backend/server
curl -I https://firebaseappcheck.googleapis.com/v1/jwks
# Should return 200 OK
# If you get 403, Google may be rate-limiting your backend IPRate limiting issues:
If your backend gets 403 Forbidden from the JWKS endpoint, add authentication:
// Use an API key to bypass rate limits
const response = await fetch(
'https://firebaseappcheck.googleapis.com/v1/jwks?key=YOUR_API_KEY',
{
headers: {
'X-goog-api-key': 'YOUR_API_KEY'
}
}
);Get your API key from Firebase Console → Project Settings → API keys
Firewall/network issues:
- Ensure your backend can reach Google APIs
- Check VPC/security group rules allow outbound HTTPS
- Verify no proxy is blocking the request
- Try from different network to isolate issue
App Check claims must be accessed correctly in security rules:
Firestore Rules:
match /databases/{database}/documents {
match /protected/{document=**} {
allow read, write: if request.firebaseAppCheck.token != null;
}
}Realtime Database Rules:
{
"rules": {
"protected": {
".read": "auth.uid != null && newData.val().appCheck != null",
".write": "auth.uid != null && newData.val().appCheck != null"
}
}
}Debugging rules:
match /test/{doc} {
allow read: if debug(request.firebaseAppCheck.token != null);
}Common errors:
- Rule expects App Check but client didn't provide token
- App Check enabled on backend but not enforced in rules
- Token expired by the time rule is evaluated
Test with simulator first, then deploy gradually to users.
How App Check token validation works: When a client app makes a request, it first obtains a token from the platform's attestation provider (Play Integrity, DeviceCheck, etc.). This token confirms the app is legitimate and running on a real device. Your backend then verifies this token by fetching the JWKS public key set, checking the signature with RS256 algorithm, and validating claims like iss (issuer), aud (audience), iat (issued time), and exp (expiration).
Token lifetime: App Check tokens are short-lived (typically 1 hour). They're automatically refreshed by the SDK, but in low-network conditions or long operations, tokens may expire. Use getToken(appCheck, true) to force a fresh token if needed.
Attestation provider differences: Play Integrity and DeviceCheck return immediate tokens and are faster. SafetyNet may have network latency. reCAPTCHA v3 is asynchronous and may delay requests. Choose based on your app's UX requirements.
Testing in production: To safely roll out App Check, use Firebase's gradual enforcement option. Start with logging-only (no enforcement), monitor success rates, then enable enforcement percentage-by-percentage. This prevents blocking all users if there's a configuration issue.
Disabling for specific routes: If certain Cloud Functions don't need App Check, omit the verification check for those endpoints. This is useful for public-facing functions or those called from web browsers where reCAPTCHA setup is complex.
Custom authentication backends: If using a custom JWT backend instead of Firebase Auth, you still need App Check tokens. Include both in your request: the user's auth token AND the App Check token in separate headers.
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
Callable Functions: UNAUTHENTICATED - Invalid credentials
How to fix "UNAUTHENTICATED - Invalid credentials" in Firebase Callable Functions