DSCODE Logo

Fix "Missing or Insufficient Permissions" Firestore Error in Flutter (2026 Guide) | DSCODE Blog

Getting PERMISSION_DENIED or "Missing or insufficient permissions" in your Flutter + Firestore app? Here's the exact checklist to debug and fix it in minutes — with real security rule examples.

# Fix "Missing or Insufficient Permissions" Firestore Error in Flutter (2026 Guide) If you're reading this, you've probably just hit this in your console: ``` [cloud_firestore/permission-denied] Missing or insufficient permissions. ``` It's one of the most common errors Flutter developers run into when connecting to Firestore — and it can show up even when your code looks 100% correct. The good news: it's almost always a **rules or auth-state issue**, not a bug in your Flutter code. This guide walks through every real cause and how to fix each one, in order of likelihood. ## Why This Error Happens Firestore checks your **Security Rules** on every single read and write — before your query even runs. If the rules evaluate to `false` for that specific request, Firestore doesn't return partial data or a warning. It just blocks the whole operation and throws `permission-denied`. So the error is never about your Dart code being "wrong." It's Firestore saying: *"the person making this request isn't allowed to do what they're asking."* ## Step 1: Check If the User Is Actually Authenticated The #1 cause is trying to read/write Firestore **before** Firebase Auth has finished initializing, or while the user is signed out. ```dart // ❌ Common mistake — querying before auth state is confirmed final user = FirebaseAuth.instance.currentUser; final data = await FirebaseFirestore.instance .collection('users') .doc(user!.uid) // user might be null here on app start! .get(); ``` On app startup, `FirebaseAuth.instance.currentUser` can be `null` for a split second while Firebase restores the session. If your Firestore call fires during that window, `request.auth` on the server side is `null`, and any rule using `request.auth.uid` will fail. **Fix — always wait for the auth state stream before querying:** ```dart StreamBuilder<User?>( stream: FirebaseAuth.instance.authStateChanges(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } if (!snapshot.hasData) { return const LoginScreen(); } return const HomeScreen(); // safe to query Firestore now }, ) ``` ## Step 2: Check Your Security Rules Actually Match Your Data Structure This is the second most common cause — rules that were written for one data shape, but the actual Firestore documents don't match it. ```js // Example: rules expect a userId field on every document match /orders/{orderId} { allow read: if request.auth.uid == resource.data.userId; } ``` If even one `orders` document is missing the `userId` field (maybe from manual testing, an import script, or an older schema version), that specific document read will fail with `permission-denied` — while every other document works fine. This is why the error can feel random. **How to debug it fast:** 1. Open Firebase Console → Firestore → Rules Playground 2. Paste in the exact document path and simulate the auth UID you're testing with 3. Toggle "Authenticated" and check the actual `request.auth.uid` value being simulated This isolates whether it's a rules logic issue or a missing-field issue in seconds, instead of guessing from Flutter logs. ## Step 3: Watch Out for Nested/Subcollection Rules A very common trap: rules on a parent collection do **not** automatically apply to subcollections. ```js // ❌ This does NOT protect /restaurants/{id}/menuItems/{itemId} match /restaurants/{restaurantId} { allow read, write: if request.auth != null; } ``` ```js // ✅ Subcollections need their own explicit match block match /restaurants/{restaurantId} { allow read, write: if request.auth != null; match /menuItems/{itemId} { allow read, write: if request.auth != null; } } ``` If you're building anything with nested data (orders → items, restaurants → menu, chats → messages), this is worth double-checking every time you add a new subcollection. ## Step 4: Custom Claims Not Refreshed on the Client If your rules check a custom claim (e.g., `request.auth.token.role == 'admin'`) and you just set that claim server-side via Cloud Functions or the Admin SDK, the Flutter client's cached ID token **won't have it yet**. The user has to get a fresh token. ```dart // Force refresh the token after a role/claim change await FirebaseAuth.instance.currentUser?.getIdToken(true); ``` Without this, users report "it works after I log out and log back in" — which is exactly this issue. ## Step 5: Double Check You're Pointing at the Right Firebase Project If you have multiple flavors (dev/staging/prod) using different `google-services.json` / `GoogleService-Info.plist` files, it's easy to accidentally deploy rules to one project while your Flutter app build is pointed at another. Run: ```bash firebase use ``` to confirm which project your rules deploy is targeting, and compare it against the `projectId` printed in your Flutter app's Firebase initialization logs. ## A Safe Starting Rule Set for Most Flutter Apps If you're just getting `permission-denied` everywhere and want a sane baseline to build from: ```js rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { allow read: if request.auth != null; allow write: if request.auth != null && request.auth.uid == userId; } match /{document=**} { allow read, write: if false; // deny by default, open up per-collection } } } ``` Never leave `allow read, write: if true;` in production — it's the single biggest cause of Firestore data breaches, and Google's own docs call it out specifically. ## Quick Debug Checklist - [ ] Is the user actually signed in when the query runs? (check `authStateChanges()`, not `currentUser` directly) - [ ] Does the specific document have the field your rule is checking? - [ ] Do subcollections have their own explicit rule blocks? - [ ] If using custom claims, has the ID token been force-refreshed? - [ ] Are your deployed rules actually on the project your app is connected to? - [ ] Did you test in Rules Playground with the real UID and document path? ## Final Thoughts `permission-denied` feels scary the first time you see it, but it's actually one of the more debuggable Firebase errors — because the cause is always in your rules or auth state, never hidden application logic. Work through the checklist above in order, and you'll almost always find it in a few minutes. --- *Need help auditing your Firebase security rules or debugging Flutter + Firebase issues in production? [DSCode](https://dscode.in) builds and scales Flutter apps backed by Firebase and Laravel — [get in touch](https://dscode.in) if you're stuck.*