Drawback
After enabling Firebase App Verify enforcement on Firestore, Storage, and RTDB, my Flutter iOS debug construct fails to acquire an App Verify token. All Firestore listeners fail with “Lacking or inadequate permissions.” The Xcode console reveals:
AppCheck failed: 'The operation could not be accomplished. The server responded with an error:
- URL: https://firebaseappcheck.googleapis.com/v1/tasks/YOUR_PROJECT/apps/YOUR_APP_ID:exchangeDebugToken
- HTTP standing code: 403
- Response physique: {
"error": {
"code": 403,
"message": "App attestation failed.",
"standing": "PERMISSION_DENIED"
}
}
Setup
- Flutter app utilizing
firebase_app_checkbundle - iOS bodily machine, debug mode by way of Xcode
AppleDebugProvider()configured in Dart:
await FirebaseAppCheck.occasion.activate(
providerApple: kDebugMode
? const AppleDebugProvider()
: const AppleAppAttestWithDeviceCheckFallbackProvider(),
);
- Firebase initialized programmatically by way of
firebase_options.dart(not utilizingGoogleService-Information.plistfor initialization) - Debug token accurately registered in Firebase console beneath the right iOS app
- Firebase App Verify API enabled in Google Cloud Console
- App Attest registered as attestation supplier in Firebase console
- Token propagation waited 30+ minutes
- Android emulator works nice with its personal debug token
What I attempted (none of those mounted it)
- Verified the debug token UUID matched precisely between Xcode logs and Firebase console
- Confirmed the Firebase App Verify API was enabled in GCP
- Waited over an hour for token propagation
- Added the App Attest entitlement to the Xcode challenge
- Configured DeviceCheck as a further supplier in Firebase console
- Set
AppCheckDebugProviderFactoryin nativeAppDelegate.swift
Root trigger
The iOS API key (present in GoogleService-Information.plist beneath the API_KEY subject, or in firebase_options.dart) has an iOS utility restriction that solely permits requests from a particular bundle ID. Each App Verify token change request should embody an X-Ios-Bundle-Identifier header.
The native Firebase iOS SDK reads the bundle ID from GoogleService-Information.plist within the app bundle. As a result of I used programmatic initialization by way of firebase_options.dart, the plist was by no means added to the Xcode construct goal. It existed on disk at ios/Runner/GoogleService-Information.plist however wasn’t referenced in challenge.pbxproj, so it was by no means copied into the app bundle.
With out the plist within the bundle, the SDK despatched requests with no bundle ID. Firebase blocked them however returned the deceptive “App attestation failed” error as an alternative of the actual purpose.
How I confirmed this
I known as the exchangeDebugToken endpoint straight with curl:
# With out bundle ID — reveals the REAL error
curl -s -X POST
"https://firebaseappcheck.googleapis.com/v1/tasks/YOUR_PROJECT/apps/YOUR_APP_ID:exchangeDebugToken?key=YOUR_API_KEY"
-H "Content material-Sort: utility/json"
-d '{"debugToken": "YOUR_DEBUG_TOKEN"}'
# Response:
# "Requests from this iOS shopper utility are blocked."
# purpose: API_KEY_IOS_APP_BLOCKED
# WITH bundle ID — succeeds
curl -s -X POST
"https://firebaseappcheck.googleapis.com/v1/tasks/YOUR_PROJECT/apps/YOUR_APP_ID:exchangeDebugToken?key=YOUR_API_KEY"
-H "Content material-Sort: utility/json"
-H "X-Ios-Bundle-Identifier: com.instance.yourapp"
-d '{"debugToken": "YOUR_DEBUG_TOKEN"}'
# Response: legitimate App Verify token returned
Substitute YOUR_PROJECT, YOUR_APP_ID, YOUR_API_KEY, and YOUR_DEBUG_TOKEN with values out of your Firebase console and Xcode logs. YOUR_APP_ID is the total app ID (e.g., 1:123456789:ios:abcdef1234567890).
Repair
Two adjustments are wanted:
1. Add GoogleService-Information.plist to the Xcode construct goal
The file probably already exists at ios/Runner/GoogleService-Information.plist — it simply is not within the Xcode challenge. In Xcode:
- Drag
GoogleService-Information.plistinto the Runner group within the challenge navigator - Uncheck “Copy gadgets if wanted” (it is already there)
- Verify the Runner goal
You possibly can confirm it is lacking by trying to find it in challenge.pbxproj:
grep -c "GoogleService-Information.plist" ios/Runner.xcodeproj/challenge.pbxproj
# If result's 0, it is not within the construct
2. Set AppCheckDebugProviderFactory in AppDelegate.swift
With the plist now within the bundle, the native Firebase SDK will auto-configure and default to DeviceCheck (or App Attest) as an alternative of the debug supplier. You need to set the debug supplier manufacturing facility earlier than Firebase configures:
import FirebaseCore
import FirebaseAppCheck
@most important
@objc class AppDelegate: FlutterAppDelegate {
override func utility(
_ utility: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let providerFactory = AppCheckDebugProviderFactory()
AppCheck.setAppCheckProviderFactory(providerFactory)
// ... remainder of your setup
GeneratedPluginRegistrant.register(with: self)
return tremendous.utility(utility, didFinishLaunchingWithOptions: launchOptions)
}
}
Observe: You would possibly see examples utilizing
#if DEBUGacross the manufacturing facility setup. Bear in mind that the Runner goal in Flutter tasks could not haveDEBUGin itsSWIFT_ACTIVE_COMPILATION_CONDITIONSconstruct setting (solely RunnerTests does). Verify your construct settings earlier than counting on#if DEBUG, or omit the guard — the Dart-sideactivate()overrides the supplier for launch builds anyway.
After each adjustments, do a clear construct (flutter clear then flutter pub get then cd ios && pod set up) and run from Xcode.
TL;DR
When you use programmatic Firebase init in Flutter (firebase_options.dart) and your iOS API key has utility restrictions, App Verify debug token change silently fails with a deceptive “App attestation failed” error. The repair: add GoogleService-Information.plist to your Xcode construct goal so the native SDK can ship the bundle ID with requests, and set AppCheckDebugProviderFactory in AppDelegate.swift earlier than plugin registration.
