App Check works at first but fails to reconnect after sleep/background
Problem
Summary Using latest Firebase v9.12 (through latest AngularFire 7.4.1) in production, with AppCheck, Firestore, and Auth. When leaving the webapp tab open and idle, the app often runs into a `403` error in POST request to exchange AppCheck token ("App attestation failed." "PERMISSION DENIED"), followed by a warning: `@firebase/app-check: AppCheck: Requests throttled due to 403 error. Attempts allowed again after 01d:00m:00s (appCheck/throttled).` --- Console Tab As you can see, the `appCheck/throttled` warning shows up twice, and is preceded by a failing 403 POST request to `content-firebaseappcheck.googleapis.com/...exchangeRecaptchaV3Token` There was no exponential backoff. Quoting from @hsubox76 in https://github.com/firebase/firebase-js-sdk/issues/6373#issuecomment-1282744902: > 403 errors are throttled for 1 day because it is likely there's something wrong with the attestation that won't be fixed with a simple retry, such as a bad API key or an attestation failure due to failing ReCAPTCHA. You can see the code for the throttling here (this also applies to 404): > https://github.com/firebase/firebase-js-sdk/blob/4eb5adfd099da3b8bd7e78bf662deda6943a8e93/packages/app-check/src/providers.ts#L286 After the failed token exchange & AppCheck throttling, Firestore can no longer load data, thus logging several "`FirebaseError: Missing or insufficient permissions.`" on all active document/collection listeners, rendering the app dysfunctional. In a second tab running the sam
Error Output
error in POST request to exchange AppCheck token ("**App attestation failed.**" "PERMISSION DENIED"), followed by a warning: `@firebase/app-check: AppCheck: Requests throttled due to 403 error. Attempts allUnverified for your environment
Select your OS to check compatibility.
1 Fix
Implement App Check Token Refresh Logic
The 403 error occurs because the App Check token becomes invalid after the app goes into sleep or background mode, leading to failed attestation. This is compounded by the throttling mechanism that prevents retries for 24 hours after a 403 error, causing Firestore access to be denied due to insufficient permissions.
Awaiting Verification
Be the first to verify this fix
- 1
Set Up App Check Listener
Create a listener that checks the app's visibility state. When the app is resumed from sleep or background, trigger a refresh of the App Check token.
typescriptimport { getAppCheck, getToken } from 'firebase/app-check'; const appCheck = getAppCheck(); function refreshAppCheckToken() { getToken(appCheck, { forceRefresh: true }) .then(token => { // Handle the new token console.log('New App Check token:', token); }) .catch(error => { console.error('Failed to refresh App Check token:', error); }); } window.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { refreshAppCheckToken(); } }); - 2
Handle Token Exchange Errors Gracefully
Implement error handling for the token exchange process to manage 403 errors without triggering throttling. This includes logging the error and providing user feedback.
typescriptfunction handleTokenExchange() { getToken(appCheck) .then(token => { // Use the token }) .catch(error => { if (error.code === 'app-check/permission-denied') { console.warn('App Check permission denied:', error); // Notify user or take action } }); } - 3
Implement Exponential Backoff for Retries
If a token exchange fails, implement an exponential backoff strategy to retry the request after a delay, instead of immediately retrying. This helps avoid hitting the throttling limit.
typescriptfunction retryTokenExchange(retries) { const maxRetries = 5; const delay = Math.pow(2, retries) * 1000; // Exponential backoff if (retries < maxRetries) { setTimeout(() => { handleTokenExchange().catch(() => retryTokenExchange(retries + 1)); }, delay); } else { console.error('Max retries reached for token exchange.'); } } - 4
Test App Check Functionality
After implementing the above changes, thoroughly test the application by putting it into sleep mode and resuming it to ensure that the App Check token refreshes correctly and that Firestore access is restored.
Validation
Confirm that the App Check token refresh logic works by monitoring the console for successful token exchanges after the app resumes from sleep. Ensure that Firestore data loads correctly without 403 errors.
Sign in to verify this fix
Environment
Submitted by
Alex Chen
2450 rep