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.64k stars 2.21k forks source link

[🐛🔥] iOS AppCheck error #6934

Closed harveyappleton closed 1 year ago

harveyappleton commented 1 year ago

Issue

Hey, firstly thank you for an amazing library, LOVE RN Firebase!

Having a strange issue with AppCheck. I'm using debug provider for local development on iOS and Android simulators. Works fine for Android, but NOT iOS when I use Firestore after initializing AppCheck.

XCode logs:

2023-02-23 20:30:28.576409+0000 myapp[83877:722951] [javascript] 'IOS: setting up appcheck...', '{\n    "IS_DEV": true\n}'
2023-02-23 20:30:28.580171+0000 myapp[83877:722269] -[RNFBAppCheckModule setTokenAutoRefreshEnabled::] [Line 84] app/isTokenAutoRefreshEnabled: __FIRAPP_DEFAULT/1
2023-02-23 20:30:28.582642+0000 myapp[83877:722269] -[RNFBAppCheckModule configureProvider::::rejecter:] [Line 74] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-23 20:30:28.582817+0000 myapp[83877:722269] -[RNFBAppCheckProviderFactory configure:providerName:debugToken:] [Line 51] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-23 20:30:28.582897+0000 myapp[83877:722269] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 35] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-23 20:30:28.582936+0000 myapp[83877:722269] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 37] appName __FIRAPP_DEFAULT
2023-02-23 20:30:48.276686+0000 myapp[83877:723015] 10.5.0 - [FirebaseFirestore][I-FST000001] AppCheck failed: 'The operation couldn’t be completed. (com.apple.devicecheck.error error 1.)'

The only JS error in logs I get is when I try and connect to Firestore via a subscriber:

[Error: [firestore/permission-denied] The caller does not have permission to execute the specified operation.]

Struggling to find any troubleshooting/docs online about either of these errors.

This only occurs on iOS.

I've tried

JS code that sets up AppCheck before any Firebase libs are called:

const provider = firebase.appCheck().newReactNativeFirebaseAppCheckProvider();
logger.log('setting up appcheck...', { IS_DEV });
await provider.configure({
  android: {
    provider: IS_DEV ? 'debug' : 'playIntegrity',
    debugToken: IS_DEV ? 'xxx9A12' : undefined
  },
  apple: {
    provider: IS_DEV ? 'debug' : 'appAttestWithDeviceCheckFallback',
    debugToken: IS_DEV ? 'xxx4F27' : undefined
  },
  isTokenAutoRefreshEnabled: !IS_DEV
});
await firebase.appCheck().initializeAppCheck({ provider, isTokenAutoRefreshEnabled: true });

(IS_DEV is just shorthand import for __DEV__)

Project Files

Javascript

Click To Expand

#### `package.json`: ```json "dependencies": { "@react-native-async-storage/async-storage": "^1.17.11", "@react-native-firebase/analytics": "^17.3.1", "@react-native-firebase/app": "^17.3.1", "@react-native-firebase/app-check": "^17.3.1", "@react-native-firebase/auth": "^17.3.1", "@react-native-firebase/crashlytics": "^17.3.1", "@react-native-firebase/dynamic-links": "^17.3.1", "@react-native-firebase/firestore": "^17.3.1", "@react-native-firebase/remote-config": "^17.3.1", "@react-navigation/native": "^6.1.5", "@react-navigation/stack": "^6.3.15", "axios": "^1.3.4", "axios-retry": "^3.4.0", "expo": "~47.0.13", "expo-av": "^13.0.2", "expo-font": "^11.0.1", "expo-haptics": "^12.0.1", "expo-linking": "^3.3.0", "expo-splash-screen": "~0.17.5", "expo-status-bar": "~1.4.2", "lodash": "^4.17.21", "lottie-ios": "3.4.1", "lottie-react-native": "^5.1.5", "nanoid": "^4.0.1", "react": "18.1.0", "react-native": "0.70.5", "react-native-deck-swiper": "^2.0.13", "react-native-device-info": "^10.4.0", "react-native-dynamic": "^1.0.0", "react-native-flash-message": "^0.4.0", "react-native-gesture-handler": "^2.9.0", "react-native-google-mobile-ads": "^9.1.2", "react-native-iap": "^12.7.3", "react-native-portalize": "^1.0.7", "react-native-restart": "^0.0.27", "react-native-safe-area-context": "^4.5.0", "react-native-screens": "^3.20.0", "react-native-svg": "^13.8.0", "react-native-vector-icons": "^9.2.0", "react-query": "^3.39.3", "use-debounce": "^9.0.3" }, "devDependencies": { "@babel/core": "^7.12.9", "@types/jest": "^29.4.0", "@types/lodash": "^4.14.191", "@types/react": "~18.0.28", "@types/react-native": "~0.70.6", "@typescript-eslint/eslint-plugin": "^5.53.0", "@typescript-eslint/parser": "^5.53.0", "babel-plugin-module-resolver": "^5.0.0", "eslint": "^8.34.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-typescript": "^17.0.0", "eslint-config-prettier": "^8.6.0", "eslint-plugin-import": "^2.27.5", "eslint-plugin-jest": "^27.2.1", "eslint-plugin-only-warn": "^1.1.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-promise": "^6.1.1", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "jest": "^29.4.3", "jest-expo": "^47.0.1", "jest-extended": "^3.2.3", "prettier": "^2.8.4", "react-native-svg-transformer": "^1.0.0", "ts-node": "^10.9.1", "typescript": "^4.9.5", "utility-types": "^3.10.0" }, ``` #### `firebase.json` for react-native-firebase v6: ```json { "react-native": { "analytics_auto_collection_enabled": false } } ```

iOS

Click To Expand

#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like: ```ruby $RNFirebaseAsStaticFramework = true # https://rnfirebase.io/#altering-cocoapods-to-use-frameworks $RNGoogleMobileAdsAsStaticFramework = true # https://docs.page/invertase/react-native-google-mobile-ads#optionally-configure-ios-static-frameworks require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking") require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods") require File.join(File.dirname(`node --print "require.resolve('@react-native-community/cli-platform-ios/package.json')"`), "native_modules") require 'json' podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} platform :ios, podfile_properties['ios.deploymentTarget'] || '13.0' install! 'cocoapods', :deterministic_uuids => false target 'myapp' do use_expo_modules! config = use_native_modules! # see https://rnfirebase.io/#altering-cocoapods-to-use-frameworks and https://docs.page/invertase/react-native-google-mobile-ads#optionally-configure-ios-static-frameworks use_frameworks! :linkage => :static # Flags change depending on the env values. flags = get_default_flags() use_react_native!( :path => config[:reactNativePath], :hermes_enabled => podfile_properties['expo.jsEngine'] == 'hermes', :fabric_enabled => flags[:fabric_enabled], # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/..", # # Uncomment to opt-in to using Flipper # Note that if you have use_frameworks! enabled, Flipper will not work # :flipper_configuration => !ENV['CI'] ? FlipperConfiguration.enabled : FlipperConfiguration.disabled, ) post_install do |installer| react_native_post_install( installer, # Set `mac_catalyst_enabled` to `true` in order to apply patches # necessary for Mac Catalyst builds :mac_catalyst_enabled => false ) __apply_Xcode_12_5_M1_post_install_workaround(installer) # This is necessary for Xcode 14, because it signs resource bundles by default # when building for devices. installer.target_installation_results.pod_target_installation_results .each do |pod_name, target_installation_result| target_installation_result.resource_bundle_targets.each do |resource_bundle_target| resource_bundle_target.build_configurations.each do |config| config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' end end end end post_integrate do |installer| begin expo_patch_react_imports!(installer) rescue => e Pod::UI.warn e end end end ``` #### `AppDelegate.m`: ```objc #import #import #import "AppDelegate.h" #import #import #import #import #import #import #if RCT_NEW_ARCH_ENABLED #import #import #import #import #import #import #import static NSString *const kRNConcurrentRoot = @"concurrentRoot"; @interface AppDelegate () { RCTTurboModuleManager *_turboModuleManager; RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; std::shared_ptr _reactNativeConfig; facebook::react::ContextContainer::Shared _contextContainer; } @end #endif @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [FIRApp configure]; [RNFBDynamicLinksAppDelegateInterceptor sharedInstance]; RCTAppSetupPrepareApp(application); RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions]; #if RCT_NEW_ARCH_ENABLED _contextContainer = std::make_shared(); _reactNativeConfig = std::make_shared(); _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; #endif NSDictionary *initProps = [self prepareInitialProps]; UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:initProps]; rootView.backgroundColor = [UIColor whiteColor]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [self.reactDelegate createRootViewController]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; [super application:application didFinishLaunchingWithOptions:launchOptions]; return YES; } - (NSArray> *)extraModulesForBridge:(RCTBridge *)bridge { // If you'd like to export some custom RCTBridgeModules, add them here! return @[]; } /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. /// /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). /// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`. - (BOOL)concurrentRootEnabled { // Switch this bool to turn on and off the concurrent root return true; } - (NSDictionary *)prepareInitialProps { NSMutableDictionary *initProps = [NSMutableDictionary new]; #if RCT_NEW_ARCH_ENABLED initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]); #endif return initProps; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } // Linking API - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options]; } // Universal Links - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result; } // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { return [super application:application didFailToRegisterForRemoteNotificationsWithError:error]; } // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; } #if RCT_NEW_ARCH_ENABLED #pragma mark - RCTCxxBridgeDelegate - (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge { _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge delegate:self jsInvoker:bridge.jsCallInvoker]; return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); } #pragma mark RCTTurboModuleManagerDelegate - (Class)getModuleClassFromName:(const char *)name { return RCTCoreModulesClassProvider(name); } - (std::shared_ptr)getTurboModule:(const std::string &)name jsInvoker:(std::shared_ptr)jsInvoker { return nullptr; } - (std::shared_ptr)getTurboModule:(const std::string &)name initParams: (const facebook::react::ObjCTurboModule::InitParams &)params { return nullptr; } - (id)getModuleInstanceFromClass:(Class)moduleClass { return RCTAppSetupDefaultModuleFromClass(moduleClass); } #endif @end ```


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:** ``` System: OS: macOS 13.2.1 CPU: (10) arm64 Apple M1 Max Memory: 124.53 MB / 32.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 16.17.1 - /var/folders/k_/gmftqzcn06x4y0lm047bj50w0000gn/T/yarn--1677184842904-0.18778201225477176/node Yarn: 1.22.19 - /var/folders/k_/gmftqzcn06x4y0lm047bj50w0000gn/T/yarn--1677184842904-0.18778201225477176/yarn npm: 8.15.0 - ~/.nvm/versions/node/v16.17.1/bin/npm Watchman: Not Found Managers: CocoaPods: 1.11.3 - /opt/homebrew/bin/pod SDKs: iOS SDK: Platforms: DriverKit 22.2, iOS 16.2, macOS 13.1, tvOS 16.1, watchOS 9.1 Android SDK: Not Found IDEs: Android Studio: 2021.3 AI-213.7172.25.2113.9123335 Xcode: 14.2/14C18 - /usr/bin/xcodebuild Languages: Java: 11.0.17 - /usr/bin/javac npmPackages: @react-native-community/cli: Not Found react: 18.1.0 => 18.1.0 react-native: 0.70.5 => 0.70.5 react-native-macos: Not Found npmGlobalPackages: *react-native*: Not Found ``` - **Platform that you're experiencing the issue on**: - [x] 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. 17.3.1` - **`Firebase` module(s) you're using that has the issue:** - `AppCheck and Firestore` - **Are you using `TypeScript`?** - Yes, `v4.9.5`


mikehardy commented 1 year ago

Curious! Appears to be configuring twice, second time without the token implying IS_DEV is no longer the expected value. What could cause that to happen? Your code snippet is perhaps not large enough to diagnose

harveyappleton commented 1 year ago

@mikehardy strange isn't it! Did some playing around, and this JS seems to work fine:

export default function App() {
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    (async () => {
      try {
        const provider = firebase.appCheck().newReactNativeFirebaseAppCheckProvider();
        logger.log('setting up appcheck...', { IS_DEV });
        await provider.configure({
          android: {
            provider: IS_DEV ? 'debug' : 'playIntegrity',
            debugToken: IS_DEV ? 'xxx9A12' : undefined
          },
          apple: {
            provider: IS_DEV ? 'debug' : 'appAttestWithDeviceCheckFallback',
            debugToken: IS_DEV ? 'xxxx4F27' : undefined
          }
        });
        await firebase.appCheck().initializeAppCheck({ provider, isTokenAutoRefreshEnabled: true });
      } catch (err) {
        logger.error('Error setting up appcheck', err);
      } finally {
        setIsLoading(false);
      }
    })();
  }, []);

  if (isLoading) return <Text>Loading...</Text>;

  return <Text>Loaded!</Text>;
}

XCode logs:

2023-02-23 23:13:19.296413+0000 myapp[3897:956831] [javascript] 'IOS: setting up appcheck...', '{\n    "IS_DEV": true\n}'
2023-02-23 23:13:19.298904+0000 myapp[3897:956426] -[RNFBAppCheckModule setTokenAutoRefreshEnabled::] [Line 84] app/isTokenAutoRefreshEnabled: __FIRAPP_DEFAULT/1
2023-02-23 23:13:19.299494+0000 myapp[3897:956426] -[RNFBAppCheckModule configureProvider::::rejecter:] [Line 74] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-23 23:13:19.299552+0000 myapp[3897:956426] -[RNFBAppCheckProviderFactory configure:providerName:debugToken:] [Line 51] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-23 23:13:19.299693+0000 myapp[3897:956426] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 35] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-23 23:13:19.299731+0000 myapp[3897:956426] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 37] appName __FIRAPP_DEFAULT

But if I use a firestore subscriber after this, I think that is what causes the error! 🤔

mikehardy commented 1 year ago

That last log line is the "debug is not going to work after this" log line. What's causing it? Really confused. We don't see this behavior in our e2e app, and contrary to your guess about firestore subscriber, the minimal repro you put in there appears enough to leave a non-debug-configured AppCheck - the firestore subscriber will just show it is not working, not be the cause of it not working

harveyappleton commented 1 year ago

Hey @mikehardy - I reduced my App.tsx file right down to this:

import React, { useEffect, useState } from 'react';
import { firebase } from '@react-native-firebase/app-check';
import { Text } from 'react-native';

export default function App() {
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    (async () => {
      try {
        const provider = firebase.appCheck().newReactNativeFirebaseAppCheckProvider();
        console.log('setting up appcheck...', { IS_DEV: __DEV__ });
        await provider.configure({
          android: {
            provider: __DEV__ ? 'debug' : 'playIntegrity',
            debugToken: __DEV__ ? 'xxxx9A12' : undefined
          },
          apple: {
            provider: __DEV__ ? 'debug' : 'appAttestWithDeviceCheckFallback',
            debugToken: __DEV__ ? 'xxxx4F27' : undefined
          }
        });
        await firebase.appCheck().initializeAppCheck({ provider, isTokenAutoRefreshEnabled: true });
      } catch (err) {
        console.error('Error setting up appcheck', err);
      } finally {
        setIsLoading(false);
      }
    })();
  }, []);

  if (isLoading) return <Text>Loading</Text>;
  return <Text>Loaded</Text>;
}

IS_DEV/__DEV__ evaluated to true so that's not the issue

Here are xcode logs, do they look correct to you?

2023-02-24 13:29:20.151229+0000 myapp[19569:1116031] [javascript] 'IOS: setting up appcheck...', '{\n    "IS_DEV": true\n}'
2023-02-24 13:29:20.153778+0000 myapp[19569:1115142] -[RNFBAppCheckModule setTokenAutoRefreshEnabled::] [Line 84] app/isTokenAutoRefreshEnabled: __FIRAPP_DEFAULT/1
2023-02-24 13:29:20.154068+0000 myapp[19569:1115142] -[RNFBAppCheckModule configureProvider::::rejecter:] [Line 74] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-24 13:29:20.154127+0000 myapp[19569:1115142] -[RNFBAppCheckProviderFactory configure:providerName:debugToken:] [Line 51] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-24 13:29:20.154213+0000 myapp[19569:1115142] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 35] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-24 13:29:20.154269+0000 myapp[19569:1115142] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 37] appName __FIRAPP_DEFAULT
harveyappleton commented 1 year ago

I'm running this on an iOS, but that shouldn't be an issue with the debug provider right?

harveyappleton commented 1 year ago

@mikehardy it appears it's an iOS simulator only issue. The whole app (far bigger than my minimal example above, which includes firestore listeners) works fine on my actual device. Here's the xcode logs from running on my device:

2023-02-25 14:22:58.714020+0000 myapp[60552:4698446] [javascript] 'IOS: setting up appcheck...', '{\n    "IS_DEV": true\n}'
2023-02-25 14:22:58.717089+0000 myapp[60552:4697722] -[RNFBAppCheckModule setTokenAutoRefreshEnabled::] [Line 84] app/isTokenAutoRefreshEnabled: __FIRAPP_DEFAULT/1
2023-02-25 14:22:58.717377+0000 myapp[60552:4697722] -[RNFBAppCheckModule configureProvider::::rejecter:] [Line 74] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-25 14:22:58.717405+0000 myapp[60552:4697722] -[RNFBAppCheckProviderFactory configure:providerName:debugToken:] [Line 51] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-25 14:22:58.717425+0000 myapp[60552:4697722] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 35] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/(not shown)
2023-02-25 14:22:58.717435+0000 myapp[60552:4697722] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 37] appName __FIRAPP_DEFAULT

do they look better to you?

mikehardy commented 1 year ago

Well, first things first:

The whole app (far bigger than my minimal example above, which includes firestore listeners) works fine on my actual device

This is a positive result and I'm happy to hear it

Sorry it's still giving you trouble in debug though. We test it all the time in our e2e harness using an intel macOS machine and iOS Simulator, and it works, so it must be possible.

I also tested it on an M1 mac and it works there (worth noting: FCM works on M1 and higher macs as well now! but that's a tangent)

So, I just don't know, I haven't run the example - haven't had time yet apologies - I'm just saying that I've personally seen the simulator work and you can see it as well in our e2e test app or checking the run logs

test config: https://github.com/invertase/react-native-firebase/blob/8c57bbb3d9c397fc472b78e9853b671ed41e30ca/packages/app-check/e2e/appcheck.e2e.js#L21-L41

test (where I actually decoded the returned JWT and checked expiry because I'm paranoid and wanted a paranoid test): https://github.com/invertase/react-native-firebase/blob/8c57bbb3d9c397fc472b78e9853b671ed41e30ca/packages/app-check/e2e/appcheck.e2e.js#L85-L91

logs: https://github.com/invertase/react-native-firebase/actions/runs/4187927928/jobs/7258409074#step:22:108

It all works. Thus my confusion, I can't imagine a scenario where this does not work - including on simulator - when configured correctly

What happens if you pull all the IS_DEV stuff out of there and just configure the provider as debug every time, with a debug token you have definitely obtained from logs (via not providing a debugtoken to start, then fishing it out) and put into firebase web console as a stored debug token?

harveyappleton commented 1 year ago

Thanks @mikehardy - it's strange isn't it. Could any of these be the cause?

I've tried redownloading the GoogleService-Info.plist file - that didn't make any difference.

This is my index.js file

import { registerRootComponent } from "expo";

import App from "./src/App";

// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);

My simplified App.tsx

import React, { useEffect } from 'react';
import { Text } from 'react-native';
import { firebase } from '@react-native-firebase/app-check';

export default function App() {
  useEffect(() => {
    (async () => {
      try {
        const provider = firebase.appCheck().newReactNativeFirebaseAppCheckProvider();
        await provider.configure({
          android: {
            provider: 'debug'
          },
          apple: {
            provider: 'debug'
          }
        });
        await firebase.appCheck().initializeAppCheck({ provider });
      } catch (err) {
        console.error(err);
      }
    })();
  }, []);

  return <Text>Loaded</Text>;
}

And from that, if I filter my xcode logs for 'AppCheck', this is what I get:

2023-02-27 21:40:49.727628+0000 myapp[78324:2495071] -[RNFBAppCheckModule setTokenAutoRefreshEnabled::] [Line 84] app/isTokenAutoRefreshEnabled: __FIRAPP_DEFAULT/1
2023-02-27 21:40:49.728578+0000 myapp[78324:2495071] -[RNFBAppCheckModule configureProvider::::rejecter:] [Line 74] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/null
2023-02-27 21:40:49.728643+0000 myapp[78324:2495071] -[RNFBAppCheckProviderFactory configure:providerName:debugToken:] [Line 51] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/null
2023-02-27 21:40:49.728686+0000 myapp[78324:2495071] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 35] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/null
2023-02-27 21:40:49.728728+0000 myapp[78324:2495071] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 37] appName __FIRAPP_DEFAULT

I was hoping to log that debug token out as you mentioned but nothing seems to appear? I'm also passing in the -FIRDebugEnabled to the debug scheme which I'm running on the sim.

It's very weird because it works fine on the Android emulator, so I don't think it's a JS/TS problem... Anything else I can try? Sorry to bother you

mikehardy commented 1 year ago

Hmm Make sure the iOS sim is 16.1+ (if I recall correctly) maybe try a toy app that's same bundle id but just react-native-firebase raw (that is, npx react-native init.. with app and app-check module If you don't supply a debugToken and you don't see a debug token pop out anywhere, that's unexpected. it should be in the logs if you search for it - item 3 here https://firebase.google.com/docs/app-check/ios/debug-provider#simulator

harveyappleton commented 1 year ago

The sim is 16.2, so that should be ok.

Just tried your suggestion, and with a blank project created using npx react-native init... and only installing RN Firebase App and AppCheck, changing the bundle ID to the same as my actual app and adding in GoogleServices-Info file, I still don't get a debug token out.

Here is the key bit of App.tsx:

function App(): JSX.Element {
  useEffect(() => {
    (async () => {
      try {
        const rnfbProvider = firebase
          .appCheck()
          .newReactNativeFirebaseAppCheckProvider();
        await rnfbProvider.configure({
          android: {
            provider: 'debug',
          },
          apple: {
            provider: 'debug',
          },
        });
        await firebase.appCheck().initializeAppCheck({
          provider: rnfbProvider,
          isTokenAutoRefreshEnabled: true,
        });
      } catch (err) {
        console.error('App check err', err);
      }
    })();
  }, []);

  const isDarkMode = useColorScheme() === 'dark';

  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
  };

  return (
    <SafeAreaView style={backgroundStyle}>
      <StatusBar
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
        backgroundColor={backgroundStyle.backgroundColor}
      />
      <ScrollView
        contentInsetAdjustmentBehavior="automatic"
        style={backgroundStyle}>
        <Header />
        <View
          style={{
            backgroundColor: isDarkMode ? Colors.black : Colors.white,
          }}>
          <Section title="Step One">
            Edit <Text style={styles.highlight}>App.tsx</Text> to change this
            screen and then come back to see your edits.
          </Section>
          <Section title="See Your Changes">
            <ReloadInstructions />
          </Section>
          <Section title="Debug">
            <DebugInstructions />
          </Section>
          <Section title="Learn More">
            Read the docs to discover what to do next:
          </Section>
          <LearnMoreLinks />
        </View>
      </ScrollView>
    </SafeAreaView>
  );
}

Here is the Xcode logs:

2023-03-01 23:13:03.059434+0000 AwesomeProject[53513:3362168] [SceneConfiguration] Info.plist contained no UIScene configuration dictionary (looking for configuration named "(no name)")
2023-03-01 23:13:03.059523+0000 AwesomeProject[53513:3362168] [SceneConfiguration] Info.plist contained no UIScene configuration dictionary (looking for configuration named "(no name)")
2023-03-01 23:13:03.135582+0000 AwesomeProject[53513:3362168] [native] Running application AwesomeProject ({
    initialProps =     {
    };
    rootTag = 1;
})
Thread Performance Checker: Thread running at QOS_CLASS_USER_INTERACTIVE waiting on a lower QoS thread running at QOS_CLASS_DEFAULT. Investigate ways to avoid priority inversions
PID: 53513, TID: 3362168
Backtrace
=================================================================
3   AwesomeProject                      0x0000000104ca7e18 -[_RCTSRRunLoopThread runLoop] + 44
4   AwesomeProject                      0x0000000104ca7b48 __49+[NSRunLoop(RCTSRWebSocket) RCTSR_networkRunLoop]_block_invoke + 116
5   libdispatch.dylib                   0x0000000106e75d5c _dispatch_client_callout + 16
6   libdispatch.dylib                   0x0000000106e77800 _dispatch_once_callout + 80
7   AwesomeProject                      0x0000000104ca7aac +[NSRunLoop(RCTSRWebSocket) RCTSR_networkRunLoop] + 84
8   AwesomeProject                      0x0000000104ca1ae8 -[RCTSRWebSocket _connect] + 68
9   AwesomeProject                      0x0000000104ca088c -[RCTSRWebSocket open] + 300
10  AwesomeProject                      0x0000000104c83184 -[RCTReconnectingWebSocket start] + 148
11  AwesomeProject                      0x0000000104c72bb8 -[RCTPackagerConnection init] + 404
12  AwesomeProject                      0x0000000104c72a04 __49+[RCTPackagerConnection sharedPackagerConnection]_block_invoke + 36
13  libdispatch.dylib                   0x0000000106e75d5c _dispatch_client_callout + 16
14  libdispatch.dylib                   0x0000000106e77800 _dispatch_once_callout + 80
15  AwesomeProject                      0x0000000104c729b8 +[RCTPackagerConnection sharedPackagerConnection] + 84
16  AwesomeProject                      0x0000000104ac64b8 -[RCTDevSettings initialize] + 148
17  AwesomeProject                      0x0000000104c5b8f0 -[RCTModuleData _initializeModule] + 96
18  AwesomeProject                      0x0000000104c5b250 -[RCTModuleData setUpInstanceAndBridge:] + 2120
19  AwesomeProject                      0x0000000104c5cd08 -[RCTModuleData instance] + 1168
20  AwesomeProject                      0x0000000104bf6220 -[RCTCxxBridge moduleForName:lazilyLoadIfNecessary:] + 680
21  AwesomeProject                      0x0000000104c65a28 -[RCTModuleRegistry moduleForName:lazilyLoadIfNecessary:] + 128
22  AwesomeProject                      0x0000000104c6599c -[RCTModuleRegistry moduleForName:] + 48
23  AwesomeProject                      0x0000000104adab40 -[RCTPerfMonitor devMenuItem] + 84
24  AwesomeProject                      0x0000000104adaa14 -[RCTPerfMonitor initialize] + 88
25  AwesomeProject                      0x0000000104c5b8f0 -[RCTModuleData _initializeModule] + 96
26  AwesomeProject                      0x0000000104c5b250 -[RCTModuleData setUpInstanceAndBridge:] + 2120
27  AwesomeProject                      0x0000000104c5cf38 __25-[RCTModuleData instance]_block_invoke + 44
28  AwesomeProject                      0x0000000104cc5bf4 RCTUnsafeExecuteOnMainQueueSync + 52
29  AwesomeProject                      0x0000000104c5cba8 -[RCTModuleData instance] + 816
30  AwesomeProject                      0x0000000104bfaa8c __49-[RCTCxxBridge _prepareModulesWithDispatchGroup:]_block_invoke + 156
31  libdispatch.dylib                   0x0000000106e74594 _dispatch_call_block_and_release + 24
32  libdispatch.dylib                   0x0000000106e75d5c _dispatch_client_callout + 16
33  libdispatch.dylib                   0x0000000106e86978 _dispatch_main_queue_drain + 1684
34  libdispatch.dylib                   0x0000000106e862d4 _dispatch_main_queue_callback_4CF + 40
35  CoreFoundation                      0x0000000180372ca4 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
36  CoreFoundation                      0x000000018036d360 __CFRunLoopRun + 1956
37  CoreFoundation                      0x000000018036c7a4 CFRunLoopRunSpecific + 584
38  GraphicsServices                    0x0000000188ff7c98 GSEventRunModal + 160
39  UIKitCore                           0x000000011260637c -[UIApplication _run] + 868
40  UIKitCore                           0x000000011260a374 UIApplicationMain + 124
41  AwesomeProject                      0x0000000104a9cbc0 main + 100
2023-03-01 23:13:03.281572+0000 AwesomeProject[53513:3362262] [connection] nw_socket_handle_socket_event [C5.1.1:1] Socket SO_ERROR [61: Connection refused]
2023-03-01 23:13:03.282121+0000 AwesomeProject[53513:3362262] [connection] nw_socket_handle_socket_event [C5.1.2:1] Socket SO_ERROR [61: Connection refused]
2023-03-01 23:13:03.282658+0000 AwesomeProject[53513:3362279] [connection] nw_connection_get_connected_socket_block_invoke [C5] Client called nw_connection_get_connected_socket on unconnected nw_connection
2023-03-01 23:13:03.282725+0000 AwesomeProject[53513:3362279] TCP Conn 0x600003c38e60 Failed : error 0:61 [61]
2023-03-01 23:13:03.326959+0000 AwesomeProject[53513:3362274] [javascript] Running "AwesomeProject" with {"rootTag":1,"initialProps":{}}
2023-03-01 23:13:03.349825+0000 AwesomeProject[53513:3362260] 10.5.0 - [FirebaseCore][I-COR000001] Configuring the default app.
2023-03-01 23:13:03.366360+0000 AwesomeProject[53513:3362260] 10.5.0 - [FirebaseCore][I-COR000033] Data Collection flag is not set.
2023-03-01 23:13:03.434442+0000 AwesomeProject[53513:3362168] -[RNFBAppCheckModule setTokenAutoRefreshEnabled::] [Line 84] app/isTokenAutoRefreshEnabled: __FIRAPP_DEFAULT/1
2023-03-01 23:13:03.434605+0000 AwesomeProject[53513:3362168] -[RNFBAppCheckModule configureProvider::::rejecter:] [Line 74] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/null
2023-03-01 23:13:03.434670+0000 AwesomeProject[53513:3362168] -[RNFBAppCheckProviderFactory configure:providerName:debugToken:] [Line 51] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/null
2023-03-01 23:13:03.434721+0000 AwesomeProject[53513:3362168] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 35] appName/providerName/debugToken: __FIRAPP_DEFAULT/debug/null
2023-03-01 23:13:03.434781+0000 AwesomeProject[53513:3362168] -[RNFBAppCheckProvider configure:providerName:debugToken:] [Line 37] appName __FIRAPP_DEFAULT

Note: this new project is on newer version of React/React Native than my actual project, but it seems to show that App Check isn't functioning happily on a blank project on a Mac M1 iOS Simulator either... 🤔 I may have cocked something up but I think with my minimal example shows it's not working as we'd expect. Can I do anything else to help assist investigations? Would love to help solve this with you.

Edit: I did pass in the -FIRDebugEnabled launch arg in the scheme in Xcode.

mikehardy commented 1 year ago

Strange - I'll try a repro off https://github.com/mikehardy/rnfbdemo/blob/main/make-demo.sh next time I get a chance. Our e2e app was created ages ago and upgraded over time, so maybe it has something in it unexpected. Either way a clean minimal thing is the way to go for me since you've done that and apparently still can't get a debug token

harveyappleton commented 1 year ago

Hey @mikehardy - any luck with this? No stress if you haven't had time yet! :-)

wmkroo commented 1 year ago

Any updates on this? I am seeing similar errors. Thanks in advance!

finn-olekahl commented 1 year ago

Same for me.. similar errors

timurbutt5053 commented 1 year ago

Same for me, also getting that error. Any updates on this?

AhmedSharaf98 commented 1 year ago

Any solution? can't connect to firebase enforced services from simulator (Happens android and ios at my end)

retireearly commented 1 year ago

+1

blarzHernandez commented 1 year ago

@mikehardy I know you are busy. Even when my error is this Error: [auth/missing-client-identifier] This request is missing a valid app identifier, meaning that neither SafetyNet checks nor reCAPTCHA checks succeeded. Please try again, or check the logcat for more details. NativeFirebaseError: [auth/missing-client-identifier] This request is missing a valid app identifier, meaning that neither SafetyNet checks nor reCAPTCHA checks succeeded. Please try again, or check the logcat for more details. I do think is related to this as well I found this today StackOverflow answer https://stackoverflow.com/questions/75603944/how-to-disable-recaptcha-in-firebase-phone-auth-otp-in-2023 so I sent the Google form for the SafetyNet Attestation API back.

natuanorg commented 1 year ago

same for me

Manikanta-GEP commented 1 year ago

does Anyone solved this issue?

harveyappleton commented 1 year ago

@mikehardy any thoughts on this? Or anything I can do to help? Thanks!

skam22 commented 1 year ago

Assuming you're using the most recent version 17.4.3, this is what you have to do to install AppCheck correctly:

edit your AppDelegate.mm file:

#import "AppDelegate.h"
#import "RNFBAppCheckModule.h"  // ⬅️ ADD THIS LINE
#import <Firebase.h>

...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

  // Initialize RNFBAppCheckModule, it sets the custom RNFBAppCheckProviderFactory
  // which lets us configure any of the available native platform providers,
  // and reconfigure if needed, dynamically after `[FIRApp configure]` just like the other platforms.
  [RNFBAppCheckModule sharedInstance];   // ⬅️ ADD THIS LINE

  [FIRApp configure];
  ...
}

you'll notice that the testing library uses the same approach to get AppCheck working for their tests.

harveyappleton commented 1 year ago

Thanks @skam22!

Hope this helps someone else: I added those lines as per skam22's suggestion.

I was able to get my debug token by also copy/pasting these lines from the testing library, uncommenting out the 2 lines which set the debug provider factory, then running the iOS app on my simulator and I was able to grab my debug token from the XCode logs.

Adding this debug token into the Firebase console, and adding that into my JS code, and commenting back out those 2 lines, and enforcing App Check again seems to work now!

Btw I'm on 17.4.3 now.

skam22 commented 1 year ago

awesome, glad it worked out for you.

skam22 commented 1 year ago

As a side note, you shouldn't have to uncomment those two lines at all.

I'll dig into it deeper later, but I think that's where the underlying problem will be. The intended setup is:

1) create a debug token on the firebase console and provide it typically via a dev .env file

APPCHECK_DEBUG_TOKEN=12345678-1234-1234-1234-123456789123

2) create a custom provider

const customProvider = appCheck().newReactNativeFirebaseAppCheckProvider();

3) configure that custom provider to use the provided debug token

await customProvider.configure({
  android: {
    provider: __DEV__ ? 'debug' : 'playIntegrity',
    debugToken: __DEV__ ? APPCHECK_DEBUG_TOKEN : undefined,
  },
  apple: {
    provider: __DEV__ ? 'debug' : 'appAttestWithDeviceCheckFallback',
    debugToken: __DEV__ ? APPCHECK_DEBUG_TOKEN : undefined,
  },
})

the fact that you're being provided with a debug token from the device/simulator (that you haven't specified) is an indication that AppCheck is being configured twice. the token that outputs in the Xcode console should be the token that you supplied during initialization if there was only one instance of AppCheck running.

@mikehardy

harveyappleton commented 1 year ago

@skam22 @mikehardy amazing thank you. Is it ok to close this issue now?

skam22 commented 1 year ago

I submitted a PR to update the installation docs - when that's merged the issue will automatically close.

davidcort commented 1 year ago

At the docs:

Use $adb logcat | grep DebugAppCheckProvider to grab your temporary secret from the android logs. The output should look lit this:

D DebugAppCheckProvider: Enter this debug secret into the allow list in the Firebase Console for your project: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

"DebugAppCheckProvider" nevers appears on the logs when i running the app with the console or Android Studio :(

efstathiosntonas commented 1 year ago

@davidcort faced the same like you today on 18.1.0 , there's no need to log the secret any more, you can ~add~ generate it directly on the Firebase console.

Go to console, tap the "Build" on the left menu and tap App Check and check the screenshots below. @mikehardy should the docs be updated and add this "alternate" solution? I know it's under there but it might be confusing that fetching the secret from logcat is the only way to go. Please don't ask me for a PR, I've had enough of firebase the last days 😢

Tap the vertical "..." next to the app: Screenshot 2023-06-23 at 11 11 30

Screenshot 2023-06-23 at 11 11 41

thoth-seshat commented 9 months ago

This still doesn't work with the iOS simulator. Is there a way to get appcheck to work on the simulator?

mikehardy commented 9 months ago

@thoth-seshat this issue was originally opened as an issue with the debug app check provider. You assert:

This still doesn't work with the iOS simulator

I disagree, it works fine. Our e2e tests uses it all the time. It works fine, as documented, on the iOS simulator

Here's the code https://github.com/invertase/react-native-firebase/blob/41c9c203a8d1f00a5462f3bf70873ff87a03b421/packages/app-check/e2e/appcheck.e2e.js#L81

This works every time locally - there's no problem with the code there and you may use it as an example