Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[firebase_app_check]: The new Firebase App Check token is not being updated in Firestore and other services that have App Check enforced. #12799

Open
1 task done
eli1stark opened this issue May 17, 2024 · 4 comments
Assignees
Labels
blocked: customer-response Waiting for customer response, e.g. more information was requested. platform: android Issues / PRs which are specifically for Android. platform: ios Issues / PRs which are specifically for iOS. plugin: app_check Stale Issue with no recent activity type: bug Something isn't working

Comments

@eli1stark
Copy link

eli1stark commented May 17, 2024

Is there an existing issue for this?

  • I have searched the existing issues.

Which plugins are affected?

App Check

Which platforms are affected?

Android, iOS

Disclaimers

  1. I was able to reproduce the issue on Android, but a similar issue exists on iOS. I just have a hard time reproducing it locally (Crashlytics shows a lot of errors with get, update, set, and listen operations, resulting in permission denied for many users on both iOS and Android). 2% of our user base is impacted by this issue and similar ones related to App Check in one way or another.

  2. This issue is reproduced in debug mode due to the nature of App Check and how difficult it is to reproduce it in a release build.

Scenario

Imagine the following scenario:

  1. I installed the brand new app (the app requires Firebase Auth to proceed further).

  2. I made a successful sign-in to the app (the app requires making a get call to Firestore to fetch the user to proceed further).

  3. I tried to fetch the user, but I was hit with the error: [cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.

  4. I started digging and thinking, "Okay, maybe it's the security rules." However, the rules were simple: allow read if auth != null, and I had just made a successful sign-in before requesting data from Firestore, so that wasn't the issue. I began wondering what else could be causing the problem, and the only other reason it could fail was because of issues with the App Check token.

  5. I thought, "What if the App Check token wasn't fetched properly before making the call to Firestore? This is plausible, maybe it's some kind of race condition." So, I decided to ensure that the token was 100% valid by fetching it beforehand. It sounded like a good idea. I made a fetch and successfully fetched the token. I then made the call to Firestore, thinking that now everything should be rock solid, but no, it threw: [cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.

  6. I started thinking again, "WHAT? Okay, I know what it is. Firestore is not picking up the newly fetched token, but why not? Isn't it how it's supposed to work?" I wondered if there was a way to pass it manually to Firestore, but it seemed like there was no way.

  7. So, here's where the issue lies: even if I ensure app attestation by fetching the token, Firestore, Cloud Functions, and Realtime Database will still fail because they won't pick up the new token until the app is restarted.

  8. Yes, you heard me right. If I hot restart the app in debug mode, it picks up the token (which is equivalent to killing the app and opening it again), but this is not a great solution because no user will do this. They will just churn and delete the app, so this is a serious issue here.

Prerequisites

  • Have a bare-bones Firebase Flutter app ready.
  • Activate App Check in main.dart as follows:
if (kDebugMode) {
  await FirebaseAppCheck.instance.activate(
    appleProvider: AppleProvider.debug,
    androidProvider: AndroidProvider.debug,
  );
} else {
  await FirebaseAppCheck.instance.activate(
    appleProvider: AppleProvider.appAttest,
  );
}

Even though it's part of the story, authentication is not needed here to simplify the case.

Firestore

Make sure your Firestore rules look like this to get them out of the way:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

Now, enforce App Check on your Firestore in Firebase.

App Setup

Have 2 buttons ready:

  1. One that makes a get request to Firestore like this:
    try {
      await FirebaseFirestore.instance.collection('path').doc('id').get(
            const GetOptions(
              source: Source.server,
            ),
          );
    } catch (e) {
      print(e);
    }
  2. Another that fetches the token like this:
    try {
      final token = await FirebaseAppCheck.instance.getToken(true);
      print(token); // ensure token is fetched properly
    } catch (e) {
      print(e);
    }

Steps to Reproduce

  1. Build the app for Android in debug mode on a real Android device, making sure you activated App Check in main.dart. Copy the App Check debug token from the debug console, you will need it later.

  2. Press the 1st button (ensure that the call fails due to App Check).

  3. Press the 2nd button (ensure it fails with a 403 error - app attestation failed).

  4. Since this is debug mode, to emulate App Check behavior, take the debug token you copied in the 1st step and add it to App Check debug tokens for Android (in Firebase).

  5. Once you've added the debug token, press the 2nd button again. The token should now be printed, indicating that you were attested by Firebase without issues.

  6. Now, press the 1st button again, and it should throw cloud_firestore/permission-denied.

  7. Hot restart the app.

  8. Press the 1st button again, it should fetch without issues.

The issue here is that it should have worked on the 6th step.

To test this multiple times, delete the app and reinstall it again so a new debug token can be generated.

Firebase Core version

2.30.1

Flutter Version

3.22.0

Relevant Log Output

No response

Flutter dependencies

Expand Flutter dependencies snippet
  firebase_core: 2.30.1
  firebase_crashlytics: 3.5.4
  firebase_analytics: 10.10.4
  firebase_dynamic_links: 5.5.4
  firebase_app_check: 0.2.2+4
  firebase_auth: 4.19.4
  firebase_database: 10.5.4
  firebase_performance: 0.9.4+4
  firebase_remote_config: 4.4.4
  cloud_firestore: 4.17.2
  cloud_functions: 4.7.3

Additional context and comments

Maybe related to:

  1. Firestore is not picking up fresh auth token and reinitialising event listeners when the current token is stale. firebase-android-sdk#5101
  2. AppCheck do not schedule an auto-refresh if a stored token exists firebase-android-sdk#5235
@eli1stark eli1stark added Needs Attention This issue needs maintainer attention. type: bug Something isn't working labels May 17, 2024
@TarekkMA TarekkMA added platform: android Issues / PRs which are specifically for Android. platform: ios Issues / PRs which are specifically for iOS. plugin: app_check labels May 20, 2024
@russellwheatley
Copy link
Member

russellwheatley commented May 21, 2024

@eli1stark - I created a pure android implementation of the same setup:https://github.com/russellwheatley/firebase-android-project

It took some time (around 5 mins) for the firestore request to be successful. This was also the case when I tested the flutter app check example app. Might be worth asking Firebase support if this is intended behaviour as it appears that this is how it works.

@russellwheatley russellwheatley added blocked: customer-response Waiting for customer response, e.g. more information was requested. and removed Needs Attention This issue needs maintainer attention. labels May 21, 2024
@eli1stark
Copy link
Author

@russellwheatley Thank you for looking into it! If that's how it works by design, then it's a big issue for apps that use App Check, because applications could potentially cut functionality for 2-3% of their user base without even realizing it. And these are legit iOS and Android devices. This is especially concerning now when Google is heavily pushing App Check with Gemini AI. For now, the only feasible option is to disable App Check and rely only on security rules. Once I disabled App Check, 95% of all permission related issues in Crashlytics went away.

What's the best way to reach out to them, is this a good link?

@google-oss-bot google-oss-bot added Needs Attention This issue needs maintainer attention. and removed blocked: customer-response Waiting for customer response, e.g. more information was requested. labels May 21, 2024
@russellwheatley
Copy link
Member

@russellwheatley Thank you for looking into it! If that's how it works by design, then it's a big issue for apps that use App Check, because applications could potentially cut functionality for 2-3% of their user base without even realizing it. And these are legit iOS and Android devices. This is especially concerning now when Google is heavily pushing App Check with Gemini AI. For now, the only feasible option is to disable App Check and rely only on security rules. Once I disabled App Check, 95% of all permission related issues in Crashlytics went away.

What's the best way to reach out to them, is this a good link?

It might be worth asking on Stack Overflow as you could get advise on how to circumvent it perhaps 🤔

@russellwheatley russellwheatley added blocked: customer-response Waiting for customer response, e.g. more information was requested. Needs Attention This issue needs maintainer attention. and removed Needs Attention This issue needs maintainer attention. labels May 22, 2024
@google-oss-bot google-oss-bot added the Stale Issue with no recent activity label May 31, 2024
@google-oss-bot
Copy link

Hey @eli1stark. We need more information to resolve this issue but there hasn't been an update in 7 weekdays. I'm marking the issue as stale and if there are no new updates in the next 7 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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked: customer-response Waiting for customer response, e.g. more information was requested. platform: android Issues / PRs which are specifically for Android. platform: ios Issues / PRs which are specifically for iOS. plugin: app_check Stale Issue with no recent activity type: bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants