invertase / react-native-firebase

πŸ”₯ A well-tested feature-rich modular Firebase implementation for React Native. Supports both iOS & Android platforms for all Firebase services.
https://rnfirebase.io
Other
11.69k stars 2.21k forks source link

Android / AppCheck - SafetyNet deprecation, Play Integrity support #6434

Closed birdofpreyru closed 1 year ago

birdofpreyru commented 2 years ago

Issue

As per Firebase App Check docs the SafetyNet provider has been deprecated, and superseded by Play Integrity. Though, RN Firebase App Check docs still read the following, and I have not found any existing issue regarding this:

This App Check module has built-in support for using the following services as attestation providers:

  • DeviceCheck on iOS
  • SafetyNet on Android

Not sure, does it require any changes in RN Firebase to support Play Integrity? Is only a matter of updating the RN Firebase documentation? Also what's up with iOS AppAttest provider?


Project Files

Javascript

Click To Expand

#### `package.json`: ```json # N/A ``` #### `firebase.json` for react-native-firebase v6: ```json # N/A ```

iOS

Click To Expand

#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like: ```ruby # N/A ``` #### `AppDelegate.m`: ```objc // N/A ```


Android

Click To Expand

#### Have you converted to AndroidX? - [ ] my application is an AndroidX application? - [ ] I am using `android/gradle.settings` `jetifier=true` for Android compatibility? - [ ] I am using the NPM package `jetifier` for react-native compatibility? #### `android/build.gradle`: ```groovy // N/A ``` #### `android/app/build.gradle`: ```groovy // N/A ``` #### `android/settings.gradle`: ```groovy // N/A ``` #### `MainApplication.java`: ```java // N/A ``` #### `AndroidManifest.xml`: ```xml ```


Environment

Click To Expand

**`react-native info` output:** ``` OUTPUT GOES HERE ``` - **Platform that you're experiencing the issue on**: - [ ] iOS - [ ] Android - [ ] **iOS** but have not tested behavior on Android - [ ] **Android** but have not tested behavior on iOS - [ ] Both - **`react-native-firebase` version you're using that has this issue:** - `e.g. 5.4.3` - **`Firebase` module(s) you're using that has the issue:** - `e.g. Instance ID` - **Are you using `TypeScript`?** - `Y/N` & `VERSION`


mikehardy commented 2 years ago

It will require quite a few changes unfortunately. Fortunately we have a year to do it! :-)

I can't just add support without doing a lot of documentation work and it will be a breaking change because play integrity requires some changes in your google account to set things up

See https://github.com/invertase/react-native-firebase/pull/6402#issuecomment-1187944539 (a comment)

also https://github.com/invertase/react-native-firebase/discussions/6345#discussioncomment-3035067

jadonhansen commented 2 years ago

@mikehardy do you foresee the process of switching from SafetyNet to Play Integrity quite a hassle for the average developer (when rnfirebase releases the breaking change) ?

Edit: I checked out the links mentioned above. Nevermind :)

mikehardy commented 2 years ago

Just for anyone else reading along, do go read those links, but just to directly answer your question @jadonhansen I don't think it will be too big a hassle, if the docs are really clear. I think that's most of the work, good documentation, screenshots of the web console changes you need to make etc. Most of the breaking changes we do hear are pretty small "do this and you are set" type changes with exception of the current use_frameworks! hassle for v15

brianGammon commented 1 year ago

Curious about something on this future App Check change. When the Play Integrity update is eventually merged and you deploy a new version of your app, will Firebase be able to process requests for devices that still have the SafetyNet version, alongside the newer build with Play? Or will it be a hard cutover, meaning anyone who hasn't updated to the latest version of your app will start seeing errors?

mikehardy commented 1 year ago

It is my understanding that you may have multiple back-end verification styles that are valid at the same time. The language around configuration is "one or more" in terms of providers, they both have a + sign for me, so I think it's soft cutover, you disable SafetyNet provider when you feel your userbase is sufficiently migrated

image

bharatbrovitech commented 1 year ago

https://www.linkedin.com/pulse/react-native-firebase-app-check-play-integrity-android-

cc : @mikehardy

mikehardy commented 1 year ago

Wow @bharatbrovitech so, instead of constructively posting a PR here, there's a linkedin article and a new native module and everything? That seems like a mal-investment, but everyone gets to spend their time doing what they like :smile:

I will say that I specifically take issue with this statement in your link:

Firebase android Safetynet is deprecated. So, rnfirebase is not useful for android anymore.

That is plainly false. You are overstating a deprecation with a migration timeline of more than a year as being "not useful". I don't get it personally, that sounds almost like fear-mongering.

I will say that I discussed this just today with the rest of the crew @ Invertase and this is on our high priority todo list. I'm going to open a PR with a sketch for it shortly, and we'll get this done.

github-actions[bot] commented 1 year ago

Hello πŸ‘‹, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

birdofpreyru commented 1 year ago

Not stale at all!

mikehardy commented 1 year ago

No, it's not. Previous stale bot broke, just installed a new one and it caught this. New stale both may or may not be respecting my "don't stale these labels..." list, so we'll see.

Either way, no intention of closing this until completed

birdofpreyru commented 1 year ago

Just to mention, according to an email sent around today by SafetyNet team:

Thus, the day AppCheck in RN-Firebase risks to turn pumpkin is probably just a month away, maybe 6.

mikehardy commented 1 year ago

Coincidentally I have tuned down most of my other work commitments and will have time to get to it before the deadline I think. I also saw that email and went "oh no - okay, gotta do this before end of January for new projects...."

tarouboy commented 1 year ago

Thank you. It's always you to save us all @mikehardy ! I couldn't help much on native coding, but is there any other ways to help? Like donation to the project?

mikehardy commented 1 year ago

I'll never say no to github sponsorships :-) and they do affect how I choose to spend my time. I will give credit to Invertase (and Google) though in general, they provide a base foundation of financial support for my work on the project which is what makes it possible for me to hack on the code here and still eat. Cheers :-)

sreehari-kt commented 1 year ago

I'm a bit confused after reading the mail, can someone help me with my doubt?

As a part of the gradual phase out of the SafetyNet Attestation API new users will no longer be able to sign up for the SafetyNet Attestation API after January 31, 2023. This includes new developers signing up through SDKs.

Does this mean that the users will not be able to sign up and use the app? If not, then what does it refers to as "New users" here?

birdofpreyru commented 1 year ago

@sreehari-kt I believe, it refers to developers, i.e. it won't be possible to set up a new app to rely on SafetyNet neither via Firebase web console nor programmatically via SDK / cli, however existing apps using it will continue to work for new end-users till future deadlines.

radekzz commented 1 year ago

Just got this warning in Google play.

I my app also don't use safetynet.

I think we should wait until Firebase do migration as currently there is nothing we can do.

But maybe there will be need for some action from Invertase as well?

grinono commented 1 year ago

Possible temporary solution > https://www.linkedin.com/pulse/react-native-firebase-app-check-play-integrity-android-/

birdofpreyru commented 1 year ago

@grinono this shameless promo has been posted above already. Taking into account that "popular dependency rnfirebase" is actively maintained, and useful far beyond the appcheck, the author of that LinkedIn post could just do a PR fixing the issue, rather than spending the same time writing an article about a workaround.

jfbourne commented 1 year ago

following :)

sanketkheni01 commented 1 year ago
diff --git a/node_modules/@react-native-firebase/app-check/android/build.gradle b/node_modules/@react-native-firebase/app-check/android/build.gradle
index f253ca2..4d0480d 100644
--- a/node_modules/@react-native-firebase/app-check/android/build.gradle
+++ b/node_modules/@react-native-firebase/app-check/android/build.gradle
@@ -89,7 +89,7 @@ repositories {
 dependencies {
   api appProject
   implementation platform("com.google.firebase:firebase-bom:${ReactNative.ext.getVersion('firebase', 'bom')}")
-  implementation "com.google.firebase:firebase-appcheck-safetynet"
+  implementation 'com.google.firebase:firebase-appcheck-playintegrity'
   implementation "com.google.firebase:firebase-appcheck-debug"
 }

diff --git a/node_modules/@react-native-firebase/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckModule.java b/node_modules/@react-native-firebase/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckModule.java
index 676d555..4e0f8b5 100644
--- a/node_modules/@react-native-firebase/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckModule.java
+++ b/node_modules/@react-native-firebase/app-check/android/src/main/java/io/invertase/firebase/appcheck/ReactNativeFirebaseAppCheckModule.java
@@ -26,7 +26,7 @@ import com.google.firebase.FirebaseApp;
 import com.google.firebase.appcheck.AppCheckProviderFactory;
 import com.google.firebase.appcheck.FirebaseAppCheck;
 import com.google.firebase.appcheck.debug.DebugAppCheckProviderFactory;
-import com.google.firebase.appcheck.safetynet.SafetyNetAppCheckProviderFactory;
+import com.google.firebase.appcheck.playintegrity.PlayIntegrityAppCheckProviderFactory;
 import io.invertase.firebase.common.ReactNativeFirebaseModule;
 import java.lang.reflect.*;

@@ -75,7 +75,7 @@ public class ReactNativeFirebaseAppCheckModule extends ReactNativeFirebaseModule

       } else {
         firebaseAppCheck.installAppCheckProviderFactory(
-            SafetyNetAppCheckProviderFactory.getInstance());
+          PlayIntegrityAppCheckProviderFactory.getInstance());
       }
     } catch (Exception e) {
       rejectPromiseWithCodeAndMessage(promise, "unknown", "internal-error", "unimplemented");

Install yarn add patch-package and create a folder named patches in project root and create a file named @react-native-firebase+app-check+16.5.0.patch and paste all the above code there. and run npx patch-package.

And follow setup instructions(Only step 1) here

BTW one pull request is already open but not yet merged. https://github.com/invertase/react-native-firebase/pull/6401

grinono commented 1 year ago

Just tried your solution but got > [appCheck/token-error] com.google.firebase.FirebaseException: No AppCheckProvider installed. Any idea where that is coming from?

sanketkheni01 commented 1 year ago

Just tried your solution but got > [appCheck/token-error] com.google.firebase.FirebaseException: No AppCheckProvider installed. Any idea where that is coming from?

calling .activate() method?

If not, then here is a hook which I created; just call this hook at the root of your project.

import {firebase} from '@react-native-firebase/app-check'
import {useEffect} from 'react'

const useAppCheck = () => {
  useEffect(() => {
    firebase
      .appCheck()
      .activate('ignore', true)
      .then(r => {
        console.log(r)
      })
  }, [])
}

export default useAppCheck
grinono commented 1 year ago

@sanketkheni01 i see it's or .activate('ignore', true) or the .getToken() method. I'm looking for the token to validate against a custom backend. after some more setup i now got

[appCheck/token-error] com.google.firebase.FirebaseException: Too many attempts.

for

await firebase.appCheck().getToken();

looks like it's related to > https://github.com/invertase/react-native-firebase/issues/6753

mikehardy commented 1 year ago

Please see the updated app-check docs on rnfirebase.io once the related PR is released (should be in a few minutes, I just merged it)

The key idea in the PR is that everyone wants a firebase-js-sdk compatible API (it is the best way to go, let's admit...) and we want unified behavior on all platforms (app-check has very different behavior across platforms at the native level). There is only one way to do that, and it is with a custom provider that acts as a facade to the underlying native providers.

So to use the Android Play Integrity provider (as well as iOS App Attest if you want...) and unify everything we have a new ReactNativeFirebaseCustomProvider for appcheck which you get, configure with some simple options, then use in the new initializeAppCheck API, and everything should work?

There is a lot of new code under the covers, which means there will inevitably be errors (apologies in advance). I'll be standing by to code up fixes + release them if anyone sees anything.

Cheers

grinono commented 1 year ago

Just checked the code > when i run >

    const appCheckForDefaultApp1 = firebase.appCheck().initializeAppCheck(
      {
        provider: {
          configure: (config) => {
            return {
              provider: 'playIntegrity',
            };
          },
        },
        isTokenAutoRefreshEnabled: true,
      },
    );

i got >

ApolloError: undefined is not an object (evaluating 'options.provider.providerOptions.android')

any idea?

mikehardy commented 1 year ago

@grinono

The config object has one provider sub-property for each platform, so it needs to be nested one deeper than you've done. You need to have the provider/playIntegrity block inside and android block. (you should also have an apple block with either deviceCheck or appAttestWithDeviceCheckFallback (or just appAttest if you have iOS14 as a minimum deploy target on your app)

I believe the example in the documentation shows the nesting with platform-specific providers I describe here?

https://rnfirebase.io/app-check/usage#configure-a-custom-provider

grinono commented 1 year ago

aaah yes missed the android object on restructuring the code but returns the same result.

await firebase.appCheck().initializeAppCheck(
      {
        provider: {
          configure: () => {
            return {
              android: {
                provider: 'playIntegrity',
              },
              // apple: {
              //   provider: 'appAttestWithDeviceCheckFallback',
              // },
              // web: {
              //   provider: 'reCaptchaV3',
              //   siteKey: 'unknown',
              // },
            };
          },
        },
        isTokenAutoRefreshEnabled: true,
      },
    );

result >

ApolloError: undefined is not an object (evaluating 'options.provider.providerOptions.android')

I checked the code but no function with .newReactNativeFirebaseAppCheckProvider();exists. that is for > "@react-native-firebase/app-check": "^17.0.0",

mikehardy commented 1 year ago

@grinono

I checked the code but no function with .newReactNativeFirebaseAppCheckProvider(); exists. that is for > "@react-native-firebase/app-check": "^17.0.0",

And yet it exists and I exercise it in our e2e suite quite a bit?

https://github.com/invertase/react-native-firebase/blob/53a436278f3c4cb23e1ddb7d2c9f14a65ce4abc1/packages/app-check/lib/index.js#L43-L45

πŸ€” perhaps this is a typescript issue - appears I've overlooked adding it to the typescript definition file so it is there in javascript but not in typescript. Can't wait until we are typescript native so 🀦 oversights like that don't happen. I'll fix that up but in the meantime can you confirm if you are using it in typescript or not? If you are and that's the problem you can reach in to the node_modules/@react-native-firebase/app-check area and add a quick type definition for that function. Takes no params, returns ReactNativeFirebaseAppCheckProvider - super-standard/easy API to define

grinono commented 1 year ago

ok, resolved it for now with // @ts-ignore

Then i was able to follow the docs page.

    // @ts-ignore
    const provider = firebase.appCheck().newReactNativeFirebaseAppCheckProvider();
    provider.configure({
      android: {
        provider: 'playIntegrity',
      }
    });

    await firebase.appCheck().initializeAppCheck({
      provider: provider,
      isTokenAutoRefreshEnabled: true, 
    });

    const tokenFB = await firebase.appCheck().getToken();
    console.log('tokenRC:', tokenFB);

but it now returns

 [appCheck/token-error] com.google.firebase.FirebaseException: Error returned from API. code: 403 body: App attestation failed.

that is for development on a real device, I tried to run adb logcat | grep DebugAppCheckProvider but this never returns a token to configure.

any idea about this error?

CHATGPT info >

"403 body: App attestation failed." is an error message from Firebase indicating that an application failed to complete the App Attestation process, which is a security feature that helps protect the data stored in Firebase. This error occurs when the app is unable to prove that it has not been tampered with. This can be due to a number of factors such as issues with the Firebase SDK, outdated dependencies, or an attempt to use a cached instance of the Firebase client. To resolve this issue, you may need to update the Firebase SDK and dependencies, or reinitialize the Firebase client.

mikehardy commented 1 year ago

@grinono

Glad that ts-ignore worked, sorry for the typescript oversight - I'm collecting follow-up items with that included over here #6886

If you want a debug token you need to use the debug provider. You may want to make your config conditional on what type of build it is, for instance - debug in non-release builds, playIntegrity in release builds? Then you might try adb logcat | grep -i appcheck to make sure the grep is loose enough to the see the token kicked out by debug provider

I appreciate the testing, thank you. Hopefully we can get this all working for you

grinono commented 1 year ago

thanks a lot, looks like everything is working in development.

toscalivia83 commented 1 year ago

In case this can help someone...

I had this error for iOS: [Error: [appCheck/token-error]...

"error": {
    "code": 403,
    "message": "App attestation failed.",
    "status": "PERMISSION_DENIED"
  }

I fixed it by searching for the "App Check debug token: xxxx" value. I added this value into the debug tokens in Firebase. Before that, I had created the debug token in Firebase and thought I needed to add it into my project, whereas this is the reverse.

When rerunning the app with this code, it worked:

rnfbProvider.configure({
        apple: {
          provider: __DEV__ ? 'debug' : 'appAttestWithDeviceCheckFallback',
          debugToken: 'debug_token_name_used_in_firebase_here,
        },
      });

      await firebase.appCheck().initializeAppCheck({
        provider: rnfbProvider,
        isTokenAutoRefreshEnabled: true,
      });

      const token = await firebase
        .appCheck()
        .getToken(true).catch(e => {
          console.log("e token", e);
        });
      console.log("token", token);
toscalivia83 commented 1 year ago

In Android, I'm getting an error saying: Possible Unhandled Promise Rejection (id: 2): Error: [appCheck/unknown] internal-error

I have created my debug token in Firebase, and added this to the configuration:

android: {
          provider: __DEV__ ? 'debug' : 'playIntegrity',
          debugToken: 'my_debug_token',
        },

Any idea why I'm getting this?

mikehardy commented 1 year ago

Possible Unhandled Promise Rejection (id: 2): Error: [appCheck/unknown] internal-error

Unsure - you might retry while following device logs with adb logcat to get more details.

toscalivia83 commented 1 year ago

If hit adb logcat | grep -i appcheck, this is what I get:

02-15 17:29:33.460 14839 14922 D RNFBAppCheck: isAppCheckCollectionEnabled via RNFBMeta: true
02-15 17:29:33.460 14839 14922 D RNFBAppCheck: isAppCheckTokenRefreshEnabled after checking app_check_token_auto_refresh: false
02-15 17:29:33.460 14839 14922 D RNFBAppCheck: isAppCheckTokenRefreshEnabled final value: false
02-15 17:29:34.146 14839 14926 D RNFBAppCheck: configureProvider - appName/providerName/debugToken: [DEFAULT]/debug/(not shown)
02-15 17:29:34.146 14839 14926 D RNFBAppCheck: ProviderFactory::configure - appName/providerName/debugToken: [DEFAULT]/debug/(not shown)
02-15 17:29:34.146 14839 14926 D RNFBAppCheck: Provider::configure with appName/providerName/debugToken: [DEFAULT]/debug/(not shown)
02-15 17:29:34.152 14839 14925 I ReactNativeJS: 'err initializeAppCheck', { [Error: [appCheck/unknown] internal-error]
mikehardy commented 1 year ago

That shows the debug provider appears to be configured, with a debug token, but it does not show the error. You might want to loosen the grep and use the same or similar string to seek in the log but not directly filter, to see if anything shows up. Double check that the token you have configured in the provider for app check is actually configured in the firebase console as a valid token for use in debug

toscalivia83 commented 1 year ago

Without the filter it doesn't give me much more details unfortunately

How can I check that the token configured in the provider is a valid token? I've generated one in Firebase and used the value directly in the provider for now. I've created 2 different tokens for iOS and Android though, I could have reused the one I just created for iOS but didn't think about this before

I've not added anything in the MainApplication in my React Native app like stated here though: https://firebase.google.com/docs/app-check/android/play-integrity-provider?authuser=0&hl=en#initialize. Do I need to do anything like this do you think?

What I can see when debugging is that the error actually comes from firebase.appCheck().initializeAppCheck

mikehardy commented 1 year ago

You should not need to do any native Java code work, we install an appcheck provider factory on android and have a custom appcheck provider which wraps the Firebase android SDK providers - delegating to the one you configure.

You might try doing the debug token configuration the opposite direction - configure the debug provider without specifying a debug token, check adb logcat to pull the generated token out and then configure that one in console and in debugToken provider options argument?

If this still is not working for whatever reason, it might pay off (in form of quick additional information) to add some log statements directly in the node_modules/@react-native-firebase/app-check/android/... java files around initializeAppCheck to see what exactly is going on + why it's failing there, maybe there is a stack trace you can dump if there is an exception (<exception>.printStackTrace(System.err) for example) ?

toscalivia83 commented 1 year ago

Thank you very much for the advice! When deleting the token from Firebase I managed to get a token in adb logcat when looking for the text com.google.firebase.appcheck.debug.internal.DebugAppCheckProvider. I added it to Firebase but unfortunately it displayed this internal-error again... I tried changing emulator but it didn't fix the issue.

However, I realized I was actually using app-check version 17.0.0. I upgraded to version 17.3.0 app-check and app dependencies, rebuilt, used directly the value in the debugToken, and it worked this way. I didn't manage to make it work with the debug token name from Firebase though, so still investigating on that...

mikehardy commented 1 year ago

Indeed all versions should always be the same (see https://invertase.io/blog/react-native-firebase-versioning) and while v17.0.0 should have worked, I did more work app-check cleanup on 17.3.0 - happy to hear that's working better as of course that's the goal with new versions ;-). 'debug token name' is not some magic thing - the idea is you replace that with a token you fish out of the logs as you did, then install it in the console, then it is a shared token you can use.

However, that's not a documented process because most of this is pretty new. If the steps:

1- install debug provider 2- run app and watch logs and get debug token from logs 3- install that logged debug token into firebase project web console 4- install that logged token as debugToken in react-native-firebase provider config

...works across multiple devices after doing it, then I can update the docs to include these steps as a reliable way to get+configure a debugToken

Can you confirm that those steps do work, and work on multiple devices (android and iOS) after pulling one token out of logs?

toscalivia83 commented 1 year ago

I don't think it works as expected yet because I'm writing the token value directly like this in my code:

rnfbProvider.configure({
          android: {
            provider: __DEV__ ? 'debug' : 'playIntegrity',
            debugToken: '48755AD4-CA4E-458D-A8CB-0C0A5C87448A',
          },
})

However if I add a token with name "token-debug-android" in Firebase = '48755AD4-CA4E-458D-A8CB-0C0A5C87448A', I would have expected to use it like this in my code, correct?:

rnfbProvider.configure({
          android: {
            provider: __DEV__ ? 'debug' : 'playIntegrity',
            debugToken: 'token-debug-android',
          },
})

Otherwise I need to replace the generatedToken in Firebase every 1h or so... Also, I have different tokens for android and for ios, isn't it?

mikehardy commented 1 year ago

However if I add a token with name "token-debug-android" in Firebase = '48755AD4-CA4E-458D-A8CB-0C0A5C87448A', I would have expected to use it like this in my code, correct?:

Nope, you need to put the actual token in there, not the name.

Otherwise I need to replace the generatedToken in Firebase every 1h or so...

Nope, once you've got a debug token that works, if you put that token into the firebase web console for the project, and put the same token as the debugToken value, it should work everywhere

Our e2e test does exactly that, for instance, and works...

toscalivia83 commented 1 year ago

Weird but why not as it's only for debugging...! This token isn't supposed to be committed into github/in the bundle, or it doesn't matter?

I double checked for iOS and Android, and with all the steps mentioned above it works fine!

Just to clarify for later, once I've done these steps and I can retrieve the token with const token = await firebase.appCheck().getToken(true);, I don't need to do anything else when my app is in production, right?

Can you confirm that those steps do work, and work on multiple devices (android and iOS) after pulling one token out of logs?

Indeed these are exactly the steps I went through. I would add that for Android the log to look for in the logs of adb logcat is Enter this debug secret into the allow list in the Firebase Console for your project and for iOS: App Check debug token: (only managed to display the logs in Xcode for iOS), because there are so many lines that we don't know what to look at. + maybe the fact that no debug token should be in Firebase BEFORE running the app and looking into the logs.

mikehardy commented 1 year ago

The expectation in general is that you manage the token's security with the level of security you need - that is an intentionally vague statement.

For react-native-firebase's e2e app for instance, we don't really care about the security. We're not validating anyway, so we just put it right in there. But for a production app with real APIs to protect where you are worried about nefarious usage from untrusted devices, you should use some sort of environmental configuration system I think that is not committed to the main repository. Basically, you should treat the shared debug token as a secret and inject it as safely as your use case / security policies require.

(this begs the question: why even allow it? Because there are already secret-management systems for react-native that need to operate at the javascript level, and this allows you to leverage those. Of course you can rely on the lower-level native debug token injection pathways that the native firebase-ios-sdk and firebase-android-sdk offer, we don't disable those, but I wanted to make it at least possible to inject a token at the javascript level, thus the optional debugToken param to the debug provider. Use it if it makes life easy without compromising security, but security analysis / secret management is project specific...)

I'll see what I can do now to improve the docs, taking your confirmation of the steps above, and keeping in mind your immediate / natural follow-on about token security

toscalivia83 commented 1 year ago

Thanks for clarifying that @mikehardy.

I thought I didn't need these debugTokens in production as they are supposed to be only in debug mode in development environment... I deleted them from my configure function when creating the Android bundle and archiving for iOS. However, it doesn't work like this in production (in Android and in iOS) and it gives a "permission-denied" error. This is what my configure function looks like:

rnfbProvider.configure({
           android: {
            provider: __DEV__ ? 'debug' : 'playIntegrity',
          },
          apple: {
            provider: __DEV__ ? 'debug' : 'appAttestWithDeviceCheckFallback',
          },
        });

Am I supposed to get the result of await firebase.appCheck().getToken(true); and use it somewhere in my code to work fine in production? In the documentation it lets me think it kind of works magically in production once we've seen it working with the debugToken in development environment... Could you give more details on this? πŸ™πŸ»

mikehardy commented 1 year ago

Thanks for clarifying that @mikehardy.

I thought I didn't need these debugTokens in production as they are supposed to be only in debug mode in development environment... I deleted them from my configure function when creating the Android bundle and archiving for iOS. However, it doesn't work like this in production (in Android and in iOS) and it gives a "permission-denied" error. This is what my configure function looks like:

rnfbProvider.configure({
           android: {
            provider: __DEV__ ? 'debug' : 'playIntegrity',
          },
          apple: {
            provider: __DEV__ ? 'debug' : 'appAttestWithDeviceCheckFallback',
          },
        });

Am I supposed to get the result of await firebase.appCheck().getToken(true); and use it somewhere in my code to work fine in production? In the documentation it lets me think it kind of works magically in production once we've seen it working with the debugToken in development environment... Could you give more details on this? πŸ™πŸ»

The debugToken won't be used with anything other than the debug provider, if you are seeing failure with the production providers they are either nit configured correctly or it's an execution environment (like, simulator/emulator) where they don't function. You'll need to triple check configurations and device logs

toscalivia83 commented 1 year ago

I made the tests on real devices in production on iOS and on Android and it didn't work.

I'm using 2 different projects for Firebase firestore: one for dev and one for production. I had the feeling it got confused at some point as I think I added the same tokens in both places but now that I used different keys it doesn't seem to behave this way anymore... I then tried as well using my development DB in production on my real device but it did not work either even though it worked with debugTokens.

If I look at the Logs Explorer or Sentry logs it doesn't give more details. It only says "Missing or insufficient permissions."

I followed all the steps listed here for iOS (deployment target is 13.0):

And here for Android:

I added no recaptcha.

I'm feeling a bit stuck now and don't know how to see what's wrong... Do you have any idea how I could fix this issue? I'd be a bit disappointed to unenforced AppCheck after all I did !

andreigaevsky commented 1 year ago

@toscalivia83 was you able to resolve your issue? It seems I have similar behavior and I checked everything with no luck.

IncognitoGK9 commented 1 year ago

I have followed this conversation for sometime now, did quite some reading on Appcheck etc, irrespective of the migration period, there seems to be gaps on what exactly is going on. In my case, with following the documentation to the letter, and adding tokens for debug, two things completely fail to work:

  1. Debugging my app on an android emulator will never work with the app check (previously off course it worked like magic), the issue here, after adding the debug token and configuring the appcheck, this error will never go away (note, only for android emulator: W/LocalRequestInterceptor(17222): Error getting App Check token; using placeholder token instead. Error: com.google.firebase.FirebaseException: Too many attempts. W/Firestore(17222): (24.7.0) [FirestoreCallCredentials]: Failed to get auth token: com.google.firebase.FirebaseException: An internal error has occurred. [ INVALID_REFRESH_TOKEN ].

Physical device testing work with no issue.

  1. Using firebase emulators with an android emulator will never work, but deploying the same app on web (be it Edge or Chrome, as long as it is web and hitting on/connecting on Firebase Emulators, WORKs with just once in a while when rebuilding the web version at times finds itself connecting to the live/production Firebase EVEN WITH NO CHANGE to firebase json settings.

My little biased take is: Firebase emulator as supposed to work with android emulators lacks a straight through configuration or has some hole for misconfiguration (call it a bug). Secondly, Appcheck (in as much as it is in Preview), lacks a straight through roadmap on configuration and actual expected use case/tests especially with Android emulators, needless to say, while adding debug tockens to the App Check interface is good for security during debugging/development, there should be a straigh way to get the android emulator device token for a developer to semi permanently have the token live in the Firebase Appcheck debug tokens for long... currently, one you enter what this line below describes in the debug tokens and restart the debugging on your app/development, YOU MUST again enter a new debug secret.... really..... (and still does not work, giving the error I mentioned earlier:

/com.google.firebase.appcheck.debug.internal.DebugAppCheckProvider(17222): Enter this debug secret into the allow list in the Firebase Console for your project: c82d9f6f-fd96-479b-a7eb-dd0d98XXXXXX

My suggestion is basic: Generate the debug secret based on a developers input (secret word, key etc), then the developer maps this at the back end of firebase, and let this stand between app restarts etc, just like app signing.

I am still frustrated with those issues, but nevertheless will work along and wait for the maturity of this products.