firebase / firebase-android-sdk

Firebase Android SDK
https://firebase.google.com
Apache License 2.0
2.28k stars 579 forks source link

FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions. #6443

Open MattSkala opened 2 weeks ago

MattSkala commented 2 weeks ago

[REQUIRED] Step 2: Describe your environment

[REQUIRED] Step 3: Describe the problem

Steps to reproduce:

We are seeing some users experiencing a crash with "io.grpc.StatusException - PERMISSION_DENIED: Missing or insufficient permissions." in Crashlytics. The full stack trace is pasted below.

We've been seeing this over the past few months over several Firebase SDK versions. It happens quite rarely and we are not able to reproduce it. It seems the exception is thrown in the Firestore background service, so we are not able to locate our code that could trigger this and there is no way to catch the exception.

Based on the analytics logs it seems to happen when we access the user's document in Firestore shortly after creating an account. On the first sigh it looks like a race condition between Firebase Auth and Firestore, but it doesn't seem to be on our side as we wait for FirebaseAuth.signInWithCredential task to complete or AuthStateListener to trigger before calling any Firestore methods.

      Fatal Exception: com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions.
       at com.google.firebase.firestore.util.Util.exceptionFromStatus(Util.java:113)
       at com.google.firebase.firestore.core.SyncEngine.notifyUser(SyncEngine.java:629)
       at com.google.firebase.firestore.core.SyncEngine.handleRejectedWrite(SyncEngine.java:511)
       at com.google.firebase.firestore.core.MemoryComponentProvider$RemoteStoreCallback.handleRejectedWrite(MemoryComponentProvider.java:135)
       at com.google.firebase.firestore.remote.RemoteStore.handleWriteError(RemoteStore.java:748)
       at com.google.firebase.firestore.remote.RemoteStore.handleWriteStreamClose(RemoteStore.java:704)
       at com.google.firebase.firestore.remote.RemoteStore.access$600(RemoteStore.java)
       at com.google.firebase.firestore.remote.RemoteStore$2.onClose(RemoteStore.java:218)
       at com.google.firebase.firestore.remote.AbstractStream.close(AbstractStream.java:365)
       at com.google.firebase.firestore.remote.AbstractStream.handleServerClose(AbstractStream.java:419)
       at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.lambda$onClose$3(AbstractStream.java:160)
       at com.google.firebase.firestore.remote.AbstractStream$CloseGuardedRunner.run(AbstractStream.java:67)
       at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.onClose(AbstractStream.java:146)
       at com.google.firebase.firestore.remote.FirestoreChannel$1.onClose(FirestoreChannel.java:173)
       at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java)
       at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java)
       at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:742)
       at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:723)
       at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
       at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
       at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:487)
       at java.util.concurrent.FutureTask.run(FutureTask.java:264)
       at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:307)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
       at com.google.firebase.firestore.util.AsyncQueue$SynchronizedShutdownAwareExecutor$DelayedStartFactory.run(AsyncQueue.java:235)
       at java.lang.Thread.run(Thread.java:1012)

 Caused by io.grpc.StatusException: PERMISSION_DENIED: Missing or insufficient permissions.
       at io.grpc.Status.asException(Status.java:541)
       at com.google.firebase.firestore.util.Util.exceptionFromStatus(Util.java)
       at com.google.firebase.firestore.core.SyncEngine.notifyUser(SyncEngine.java:629)
       at com.google.firebase.firestore.core.SyncEngine.handleRejectedWrite(SyncEngine.java:511)
       at com.google.firebase.firestore.core.MemoryComponentProvider$RemoteStoreCallback.handleRejectedWrite(MemoryComponentProvider.java:135)
       at com.google.firebase.firestore.remote.RemoteStore.handleWriteError(RemoteStore.java:748)
       at com.google.firebase.firestore.remote.RemoteStore.handleWriteStreamClose(RemoteStore.java:704)
       at com.google.firebase.firestore.remote.RemoteStore.access$600(RemoteStore.java)
       at com.google.firebase.firestore.remote.RemoteStore$2.onClose(RemoteStore.java:218)
       at com.google.firebase.firestore.remote.AbstractStream.close(AbstractStream.java:365)
       at com.google.firebase.firestore.remote.AbstractStream.handleServerClose(AbstractStream.java:419)
       at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.lambda$onClose$3(AbstractStream.java:160)
       at com.google.firebase.firestore.remote.AbstractStream$CloseGuardedRunner.run(AbstractStream.java:67)
       at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.onClose(AbstractStream.java:146)
       at com.google.firebase.firestore.remote.FirestoreChannel$1.onClose(FirestoreChannel.java:173)
       at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java)
       at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java)
       at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:742)
       at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:723)
       at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
       at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
       at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:487)
       at java.util.concurrent.FutureTask.run(FutureTask.java:264)
       at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:307)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
       at com.google.firebase.firestore.util.AsyncQueue$SynchronizedShutdownAwareExecutor$DelayedStartFactory.run(AsyncQueue.java:235)
       at java.lang.Thread.run(Thread.java:1012)
milaGGL commented 2 weeks ago

Hi @MattSkala, thank you for reporting this issue. Could you please enable debug level logging and see if it is possible to capture more info on the crash?

google-oss-bot commented 1 week ago

Hey @MattSkala. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

MattSkala commented 1 week ago

Could you please enable debug level logging and see if it is possible to capture more info on the crash?

Hi, as mentioned above, unfortunately, we are not able to reproduce the issue locally. We just get crash reports from a small percentage (but still a considerable amount) of our users.

I can just add that the Crashlytics issue ID cf4e5375be64dd352e247f10f18c271c, and our project number 499050530164, in case it helps in any way.

milaGGL commented 1 week ago

Hi @MattSkala, unfortunately, I cannot access the crashlytics log. And the log provided indicates that it is failing to write into firestore (handleRejectedWrite) due to permissions issue. So high likely, it failed at the "creating an account" stage, not "accessing user's document " stage later.

It will be more efficient if you can file a firebase support ticket, provide the Crashlytics issue ID, project Id, and if possible a timestamp when the issue happened, so that firebase team/ backend team can get a closer look at the server logs.

Out of curiosity, what is the security rule the project is using?

MattSkala commented 1 week ago

So high likely, it failed at the "creating an account" stage, not "accessing user's document " stage later.

I just checked that all the Crashlytics logs have the ID of the authenticated user attached, so it seems that the account was created successfully, but then it failed when writing the user document.

It will be more efficient if you can file a firebase support ticket, provide the Crashlytics issue ID, project Id, and if possible a timestamp when the issue happened, so that firebase team/ backend team can get a closer look at the server logs.

Thanks for the suggestion, I've also created a support ticket (Case 10321781).

Out of curiosity, what is the security rule the project is using?

We only allow the authenticated user to read their own document:

    match /users/{user_id} {
      allow read, write: if request.auth.uid == user_id;
    }
milaGGL commented 1 week ago

interesting, would it be possible to check "request.auth.uid == user_id" condition before sending the request? could it be one of the id is still null when the request is made?