DEV Community

Cover image for How to Fix Firebase `PERMISSION_DENIED` Errors in React Native
Asta Silva
Asta Silva

Posted on

How to Fix Firebase `PERMISSION_DENIED` Errors in React Native

If you are using Firebase with a React Native app, sooner or later you will probably run into something like this:

FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
Enter fullscreen mode Exit fullscreen mode

Or:

Error: PERMISSION_DENIED: Permission denied
Enter fullscreen mode Exit fullscreen mode

And the annoying part is that the error does not tell you much.

It does not say:

Your Firestore rule on this collection is blocking the request.

It just tells you that Firebase rejected it.

That can happen for several different reasons. Your Firestore rules might be doing exactly what you told them to do, your user might not actually be authenticated, App Check might be involved, or your app might be connected to a different Firebase project than you think.

So before changing random rules until the error disappears, it is better to figure out why Firebase is rejecting the request.


First, find out which Firebase service is rejecting the request

PERMISSION_DENIED is not limited to Firestore.

You can see permission-related errors with services such as:

  • Cloud Firestore
  • Realtime Database
  • Firebase Storage
  • Firebase App Check
  • Firebase Authentication-related operations

So start by looking at the full error.

For example:

FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
Enter fullscreen mode Exit fullscreen mode

If the stack trace shows a Firestore operation such as:

firestore()
  .collection("users")
  .doc(userId)
  .get();
Enter fullscreen mode Exit fullscreen mode

then your first place to look is Firestore security rules.

If the error happens while uploading a file, Storage rules are more relevant.

This sounds obvious, but a long React Native stack trace can make it easy to focus on the wrong part.


Check your Firestore security rules

If the error comes from Firestore, check your rules first.

For example, you might have:

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {

    match /users/{userId} {
      allow read, write: if request.auth != null;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This rule only allows authenticated users to access documents under:

/users/{userId}
Enter fullscreen mode Exit fullscreen mode

If your app makes the request before the user has signed in, Firebase will reject it.

That can produce:

permission-denied
Enter fullscreen mode Exit fullscreen mode

The important part is that Firebase is not necessarily malfunctioning here.

Your rule may simply be doing exactly what it was written to do.


Check whether the user is actually authenticated

One of the easiest mistakes is assuming that the user is signed in when the request runs.

For example:

const user = auth().currentUser;

console.log(user);
Enter fullscreen mode Exit fullscreen mode

If that prints:

null
Enter fullscreen mode Exit fullscreen mode

then there is no authenticated Firebase user at that moment.

A rule such as:

allow read, write: if request.auth != null;
Enter fullscreen mode Exit fullscreen mode

will reject the request.

This can happen when your app starts up and immediately tries to read Firestore before Firebase Authentication has restored the user's session.

For example, you might have code that effectively does this:

const user = auth().currentUser;

firestore()
  .collection("users")
  .doc(user.uid)
  .get();
Enter fullscreen mode Exit fullscreen mode

If user is not available yet, the request can fail.

Instead, make sure your application waits for the authentication state before making requests that require authentication.

With React Native Firebase, you can listen for authentication changes:

useEffect(() => {
  const unsubscribe = auth().onAuthStateChanged(user => {
    if (user) {
      // Safe to make requests that require authentication
    }
  });

  return unsubscribe;
}, []);
Enter fullscreen mode Exit fullscreen mode

The exact structure depends on your app, but the idea is the same:

Do not assume authentication has finished restoring just because the app has started.


Check that the rule matches the path you are actually using

This is another very common one.

Imagine your rules contain:

match /users/{userId} {
  allow read, write: if request.auth != null;
}
Enter fullscreen mode Exit fullscreen mode

But your app actually accesses:

/profiles/123
Enter fullscreen mode Exit fullscreen mode

That rule does not apply to /profiles/123.

You need a matching rule for the path being accessed.

This becomes especially confusing when you have nested collections.

For example:

/users/123/settings/theme
Enter fullscreen mode Exit fullscreen mode

is not the same path as:

/users/123
Enter fullscreen mode Exit fullscreen mode

Your rules need to match the actual document path.

If you are getting PERMISSION_DENIED, look at the exact collection and document your code is trying to access.

Do not assume the rule you are looking at is the one Firebase is evaluating.


Be careful with nested Firestore documents

Suppose you have:

users/{userId}/settings/{settingId}
Enter fullscreen mode Exit fullscreen mode

You might write:

match /users/{userId} {
  allow read, write: if request.auth != null;
}
Enter fullscreen mode Exit fullscreen mode

and expect that to cover everything underneath the user.

It does not automatically mean that every nested document is covered.

You may need a recursive wildcard depending on the structure and rules you want.

For example:

match /users/{userId}/{document=**} {
  allow read, write: if request.auth != null;
}
Enter fullscreen mode Exit fullscreen mode

Be careful with this kind of rule, though.

It can give access to much more data than you intended.

Do not add a broad wildcard just because it makes the error disappear.


Do not use allow read, write: if true as the final fix

You will sometimes see people recommend this while debugging:

allow read, write: if true;
Enter fullscreen mode Exit fullscreen mode

Yes, it can make the permission error disappear.

It also allows anyone who can access your Firebase project from the client to read and write the matching data.

That is not a real fix.

If you temporarily use an open rule to confirm that the rules are the cause, put the secure rule back immediately afterward.

For example, if the app should only allow authenticated users:

allow read, write: if request.auth != null;
Enter fullscreen mode Exit fullscreen mode

is very different from:

allow read, write: if true;
Enter fullscreen mode Exit fullscreen mode

The goal is not simply to make Firebase stop complaining.

The goal is to make the rule match what your application is actually supposed to allow.


Check whether you are using the correct Firebase project

This one can waste a lot of time.

Your React Native app might be connected to a different Firebase project than the one you are looking at in the Firebase Console.

You think you are checking the rules for:

my-production-project
Enter fullscreen mode Exit fullscreen mode

but the app is actually connected to:

my-development-project
Enter fullscreen mode Exit fullscreen mode

Now you change rules in the console and nothing happens.

The app keeps returning:

permission-denied
Enter fullscreen mode Exit fullscreen mode

because you are fixing the wrong project.

Check your Firebase configuration and make sure the project ID matches the Firebase project you are inspecting.

For Android, check the Firebase configuration used by your Android app.

For iOS, check the corresponding iOS Firebase configuration.

If you have development, staging, and production projects, this becomes especially important.


Check your Android and iOS Firebase configuration

If the problem only happens on one platform, compare the Firebase configuration between them.

For Android, make sure the correct Firebase configuration is being used.

For iOS, make sure the correct configuration is being used there as well.

A situation like this is possible:

Android → Firebase project A
iOS     → Firebase project B
Enter fullscreen mode Exit fullscreen mode

You can then spend hours changing Firestore rules in project A while testing an iOS build that is actually talking to project B.

If the error happens only on Android or only on iOS, platform-specific Firebase configuration should be on your checklist.


Check Firebase App Check

App Check can also be involved when Firebase rejects requests.

App Check is designed to help verify that requests are coming from your legitimate app rather than an unauthorized client.

If you recently enabled App Check or changed its configuration, check whether the failing request is related to App Check.

This becomes especially important if:

  • the app worked before enabling App Check
  • requests work in one environment but not another
  • you are testing a development build
  • you changed App Check enforcement settings
  • you changed the app's Android or iOS configuration

Do not immediately disable App Check just because it is involved.

First determine whether the request is failing because of App Check configuration or because of your Firestore/Storage rules.

Those are different problems.


Development and production can behave differently

A common source of confusion is:

"It works in development, but my production build gets PERMISSION_DENIED."

That does not necessarily mean the Firebase rules randomly changed.

Your development and production builds may be using different:

  • Firebase projects
  • application IDs
  • bundle identifiers
  • configuration files
  • App Check settings
  • authentication settings
  • environment variables

If the problem only happens in a release build, compare the Firebase configuration between the working and failing builds.

This is usually more useful than changing your Firestore rules blindly.


Check the exact user ID being used

Suppose your rule is intended to allow users to access their own documents:

match /users/{userId} {
  allow read, write: if request.auth.uid == userId;
}
Enter fullscreen mode Exit fullscreen mode

That means this request:

/users/123
Enter fullscreen mode Exit fullscreen mode

will only work if:

request.auth.uid == "123"
Enter fullscreen mode Exit fullscreen mode

If your application accidentally requests:

/users/456
Enter fullscreen mode Exit fullscreen mode

while the logged-in user is actually 123, Firebase will reject it.

So log the values you are using while debugging:

console.log("Current user:", auth().currentUser?.uid);
console.log("Requested user:", userId);
Enter fullscreen mode Exit fullscreen mode

If they do not match, you have found the problem.

This is much more useful than changing the security rule to allow everyone.


Check your Storage rules if the error happens during uploads

If the problem happens while uploading an image, PDF, or another file, you may actually be dealing with Firebase Storage rather than Firestore.

For example:

FirebaseError: [storage/unauthorized] User is not authorized to perform the desired action.
Enter fullscreen mode Exit fullscreen mode

Storage has its own security rules.

A Firestore rule like:

allow read, write: if request.auth != null;
Enter fullscreen mode Exit fullscreen mode

does not control Firebase Storage.

If the error happens during:

storage()
  .ref("profile-images/avatar.jpg")
  .putFile(filePath);
Enter fullscreen mode Exit fullscreen mode

look at your Storage rules instead.

The service producing the error matters.


Check Realtime Database rules separately

The same idea applies to Realtime Database.

Realtime Database has its own rules structure, for example:

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}
Enter fullscreen mode Exit fullscreen mode

Changing Firestore rules will not fix a Realtime Database permission problem.

If your application uses several Firebase services, make sure you know which one is rejecting the request before changing anything.


Use the Firebase Emulator when appropriate

If you are working on complicated security rules, the Firebase Emulator Suite can make debugging much easier.

Instead of repeatedly testing rules against production data, you can test your rules locally.

This is especially useful when your rules depend on things like:

request.auth
request.auth.uid
request.resource.data
resource.data
Enter fullscreen mode Exit fullscreen mode

For example, a rule might look correct at first glance but fail because the incoming document does not contain the field your rule expects.

Testing the rule with known authentication and data values makes these problems much easier to see.


Look at what changed right before the error

This is one of the most useful debugging habits for Firebase errors.

Ask:

What changed immediately before PERMISSION_DENIED appeared?

Maybe you:

  • changed Firestore rules
  • added authentication
  • changed the user document structure
  • switched Firebase projects
  • enabled App Check
  • changed an Android package name
  • changed an iOS bundle identifier
  • updated a Firebase dependency
  • moved a Firestore collection
  • changed the document ID
  • created a new production build

The timing does not prove what caused the error, but it gives you a very good place to start.


A simple debugging order

When I get a Firebase PERMISSION_DENIED error, I usually go through these checks:

1. Identify the Firebase service

Is it:

  • Firestore?
  • Storage?
  • Realtime Database?
  • App Check?
  • something else?

2. Read the exact path

What collection, document, or storage path is the app trying to access?

3. Check authentication

console.log(auth().currentUser?.uid);
Enter fullscreen mode Exit fullscreen mode

Is there actually a logged-in user?

4. Check the security rule

Does the rule actually match the path being accessed?

5. Check the values used by the rule

If the rule compares user IDs, roles, or document fields, verify those values.

6. Check the Firebase project

Make sure the app and Firebase Console are using the same project.

7. Check App Check

Especially if the problem started after enabling or enforcing it.

8. Check platform-specific configuration

If only Android or iOS is affected, compare the configuration for that platform.

9. Reproduce the problem again

After making one change, test again.

This gives you a much clearer debugging trail.


Example: finding a simple rules mistake

Imagine your Firestore rule says:

match /users/{userId} {
  allow read: if request.auth != null
               && request.auth.uid == userId;
}
Enter fullscreen mode Exit fullscreen mode

Your app is logged in as:

abc123
Enter fullscreen mode Exit fullscreen mode

but your code requests:

/users/undefined
Enter fullscreen mode Exit fullscreen mode

Firebase sees:

request.auth.uid == "abc123"
Enter fullscreen mode Exit fullscreen mode

and:

userId == "undefined"
Enter fullscreen mode Exit fullscreen mode

The condition is false.

So Firebase returns:

permission-denied
Enter fullscreen mode Exit fullscreen mode

The problem is not Firebase.

The problem is that your application generated the wrong document path.

This is why checking the exact request is so important.


Final thoughts

PERMISSION_DENIED can be frustrating because the error itself is often very short while the number of possible causes is not.

The important thing is to avoid treating it as simply:

"Firebase permissions are broken."

Start by finding out which Firebase service rejected the request, then check the exact path, authentication state, security rules, and Firebase project.

If the error appeared after a recent change, use that as a clue.

And avoid the temptation to solve everything with:

allow read, write: if true;
Enter fullscreen mode Exit fullscreen mode

That only hides the real problem and can leave your data exposed.

If your error is buried inside a much larger React Native/Firebase log and it is difficult to tell whether the problem is authentication, rules, App Check, configuration, or something else, you can paste the full error into FixMyError and use it to narrow down what is actually going wrong.

Try it at https://www.fixmyerrorapp.com.

Top comments (0)