firebase-android-sdk: Firestore is not picking up fresh auth token and reinitialising event listeners when the current token is stale.

[READ] Step 1: Are you in the right place?

Issues filed here should be about bugs in the code in this repository. If you have a general question, need help debugging, or fall into some other category use one of these other channels:

  • For general technical questions, post a question on StackOverflow with the firebase tag.
  • For general Firebase discussion, use the firebase-talk google group.
  • For help troubleshooting your application that does not fall under one of the above categories, reach out to the personalized Firebase support channel.

[REQUIRED] Step 2: Describe your environment

  • Android Studio version: Android Studio Flamingo | 2022.2.1 Patch 2
  • Firebase Component: _____ Firestore, Auth
  • Component version: Android BOM 32.0.0

[REQUIRED] Step 3: Describe the problem

We have this issue on the FlutterFire repository: https://github.com/firebase/flutterfire/issues/11146 There’s also an active SO issue here: https://stackoverflow.com/questions/76436462/firebase-logged-in-user-with-expired-auth-id-token?noredirect=1

To summarise:

  1. The user logs in via Auth SDK. Firestore snapshot event listeners are attached.
  2. The app goes into the background for a lengthy period of time (in the range of 5-10 hours).
  3. Whilst the user is still logged in, the event listeners start firing exceptions with “permission-denied” exceptions.
  4. Requires manually reattaching the Firestore snapshot listeners to make use of the fresh auth token.

My understanding and after discussion with @puf, Firestore ought to pick up the fresh auth token under the hood, and reattach the snapshot event listeners without the user having to do this step manually.

Steps to reproduce:

Not an easy one to reproduce as it requires a lengthy time period, but following on from the summary:

  1. Implement Firestore event listener that requires level of auth access via Firestore rules.
  2. Log user in via Auth with access to the data being listened to.
  3. Put app in background for >= 10 hours (Might be worth just letting it run for the day and coming back to it.)
  4. Bring app to foreground, add data to that aspect of data being listened to on snapshot listener, and wait for a permission-denied error via logcat.

The user mentioned in the FlutterFire issue that the problem occurs when the app is brought to the foreground but I have asked for confirmation whether it occurs when the app is also in a background state as I suspect it does. This would remove the variable of app state from the reproduction.

Relevant Code:

Create a barebones app on android studio and implement simple Auth login with Firestore event listener following steps mentioned above.

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 1
  • Comments: 35 (16 by maintainers)

Most upvoted comments

I was able to reproduce. I am trying a couple of things that might effect the behaviour. Not sure how long it will take as I have to test with the release version via Play store for changes and it takes a while for the error to appear. Will keep this channel updated with any notable updates 👍

Hi @RamsayGit, once I added App Check to my own Android app built with the Firebase Android SDK, I started producing a similar behavior, though in my case logging in again seemed to solve the problem consistently. I do believe this is related to app check, and I have shared it with that team. However I do intend to take another look in the next week to see if I can move it forward.

Not sure if it will be useful to you at this point, but there is an API to listen for AppCheck token state changes.

Hi, there. We’re reasonably sure we’ve identified what is causing the issue. It seems there is a difficulty translating the suspected API to a pure android reproducible implementation which would allow the issue to be resolved. Bear with us, we haven’t given up, it’s a rather difficult one to fix given the time frame the error pops up. The last time I reproduced the bug, it took 3 days for the error to appear. Thank you for your patience 🙏

Hello @russellwheatley, do you have any news regarding the issue ?

@russellwheatley @MarkDuckworth The initial problem I faced was that after resuming the app after an extended period (a day or two), all long-lived snapshots would stop updating, even though new reads and writes would still go through. I managed to apply a temporary patch to the issue by recreating all snapshot listeners when the app resumes. The following is an example of how this was done:

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      _attachListener();
    }
  }

  void _attachListener() {
    _hangoutSubscription?.cancel();
    _hangoutSubscription = FirebaseFirestore.instance
        .collection(FirePath.hangouts)
        .where....
        .snapshots(includeMetadataChanges: true)
        .transform(ignorePermissionDeniedTransformer())
        .listen((snapshot) {
          ...
        }, onError: (error) {
          FirebaseCrashlytics.instance.recordError(
            error,
            StackTrace.current,
            reason: 'Error in AdminHangoutRepository',
          );
        });
  }
}

This solution seemed to fix the issue in production for a few days, but today a new variant of the problem appeared. This time, the issue seems to be that the token didn’t refresh, and yet the user remained logged in. I have this code in place to log out the user, but it didn’t trigger:

_authStateSubscription =
    FirebaseAuth.instance.authStateChanges().listen((user) {
  if (user == null) {
    set(RouteState.login()); //did not fire when auth id token expired
  }
});

As shown in this video I captured from my production app today, nothing updates in response to my read and write requests. Connecting my phone to logcat indeed showed permission errors:

 16:29:15.551 19076 Firestore        com.letshang.app        W  (24.6.0) [z0]: (74f4cfa) Stream closed with status: j1{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}.
16:29:15.564 19076 Firestore        com.letshang.app        W  (24.6.0) [Firestore]: Write failed at users_private/JDGWvPLVtNTcrb: j1{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}
16:46:09.985 19076 Firestore        com.letshang.app        W  (24.6.0) [Firestore]: Listen for Query(target=Query(hangouts where statein[pre_open,open] order by geohash_6, __name__);limitType=LIMIT_TO_FIRST) failed: j1{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}

Despite these permission errors, the user remains logged in and authStateChanges doesn’t get called. I’m currently unsure how to patch this until Firebase addresses this fundamental issue. A potential workaround could be calling user.getIdTokenResult() on each app resume, or perhaps performing a single write every few hours on app resume to check for the permission denied error, and then automatically log out the user if it’s encountered.

Please let me know if there are any potential issues with my approach to detecting when the user should be logged out, or if you have found a more effective temporary workaround for this issue.

@russellwheatley, I’ll work on a reproduction and see if we can get this fixed.