Summary
The [cloud_firestore/permission-denied] error persisted even after setting completely open Firestore rules. The root cause was mis‑aligned Firebase project configuration between the app and the console, specifically using the default Firebase App ID instead of the named app instance generated by flutterfire configure. Once the correct app options were applied and the app was re‑installed, the permission error disappeared.
Root Cause
- Incorrect Firebase App initialization
- The app was initializing the default FirebaseApp while the generated
firebase_options.dartcontained a named app (DefaultFirebaseOptions.currentPlatform). - This caused the client to connect to a different project (or a different app within the same project) that still had restrictive rules.
- The app was initializing the default FirebaseApp while the generated
- Stale authentication token
- When the wrong app instance is used, the token does not match the rules, leading Firestore to reject every request.
Why This Happens in Real Systems
- Multiple Firebase projects or environments (dev, staging, prod) share similar bundle IDs / package names, making it easy to point a build at the wrong backend.
- The FlutterFire CLI generates named app options to avoid collisions, but developers often call
Firebase.initializeApp()without passing those options, falling back to the default configuration. - Hard‑coded
google-services.json/GoogleService-Info.plistfiles can become out‑of‑sync after a new project is created or duplicated.
Real-World Impact
- Complete loss of read/write capability across all platforms, even when rules are intentionally open.
- User frustration and increased support tickets, as the UI shows generic “permission denied” errors without obvious cause.
- Wasted debugging time spent on rules, network logs, and platform‑specific settings rather than the actual misconfiguration.
Example or Code (if necessary and relevant)
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
How Senior Engineers Fix It
- Validate project linkage
- Run
flutterfire configureagain and confirm the generatedfirebase_options.dartmatches the intended Firebase project. - Verify that
google-services.jsonandGoogleService-Info.plistare regenerated after any project change.
- Run
- Explicitly pass the correct options to
Firebase.initializeApp(as shown above) instead of relying on the default. - Delete the app from the device/emulator before reinstalling to clear stale auth tokens.
- Add a sanity check in code:
final app = Firebase.app(); print('Firebase project: ${app.options.projectId}');This quickly reveals mismatches.
- Enable Firebase Debug logging (
firebase_debug_mode: true) to see the exact project ID the SDK is using during runtime. - Lock down rules after verification once the correct project is confirmed.
Why Juniors Miss It
- Assume Firebase.initializeApp() always picks the right project, overlooking the need to pass the generated options.
- Focus on rule syntax rather than checking the actual network endpoint the app contacts.
- Skip cleaning the device after major config changes, leaving old tokens that mask the real issue.
- Rely on IDE autocomplete that may insert a default
initializeApp()call, silently overriding the CLI‑generated configuration.