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.71k stars 2.22k forks source link

[πŸ›] Phone Auth MacCatalyst Returns [Token Mismatch] error #6718

Closed ZComwiz closed 1 year ago

ZComwiz commented 1 year ago

Issue

Trying to sign in on Mac Catalyst with Phone Auth following Documentation returns the sign error:

[auth/invalid-app-credential] Token mismatch

Describe your issue here


Project Files

Podfile:

require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

platform :ios, '13.0'
install! 'cocoapods', :deterministic_uuids => false

production = ENV["PRODUCTION"] == "1"

# used for automatic bumping
flipperkit_version = '0.138.0'

target 'App' do
    config = use_native_modules!

    # Flags change depending on the env values.
    flags = get_default_flags()

    use_react_native!(
        :path => config[:reactNativePath],
        # to enable hermes on iOS, change `false` to `true` and then install pods
        :production => production,
        :hermes_enabled => true,
        :fabric_enabled => flags[:fabric_enabled],
        :flipper_configuration => FlipperConfiguration.enabled,
        # An absolute path to your application root.
        :app_path => "#{Pod::Config.instance.installation_root}/.."
)   

    permissions_path = '../node_modules/react-native-permissions/ios'

    # TODO: Remove Firebase lines once `pod install` works without them
    pod 'Firebase', :modular_headers => true
    pod 'FirebaseCore', :modular_headers => true
    pod 'GoogleUtilities', :modular_headers => true
    $RNFirebaseAsStaticFramework = true

    target 'AppTests' do
        inherit! :complete
        # Pods for testing
    end

    post_install do |installer|
        react_native_post_install(installer)
        __apply_Xcode_12_5_M1_post_install_workaround(installer)
    end
end

Javascript:

package.json:

        "@react-native-firebase/analytics": "16.4.5",
        "@react-native-firebase/app": "16.4.5",
        "@react-native-firebase/auth": "16.4.5",
        "@react-native-firebase/crashlytics": "16.4.5",
        "@react-native-firebase/in-app-messaging": "16.4.5",
        "@react-native-firebase/messaging": "16.4.5",
        "@react-native-firebase/remote-config": "16.4.5",

firebase.json for react-native-firebase v6:

# 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.mm`: ```objc #import "AppDelegate.h" #import #import #import #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 { RCTAppSetupPrepareApp(application); RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; [self configureFirebase]; UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; [AppCenterReactNative register]; #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]; RCTRootView *rootView = (RCTRootView *)RCTAppSetupDefaultRootView(bridge, @"App", initProps); if (@available(iOS 13.0, *)) { rootView.backgroundColor = [UIColor systemBackgroundColor]; } else { rootView.backgroundColor = [UIColor whiteColor]; } self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; #if TARGET_OS_MACCATALYST self.window.windowScene.titlebar.titleVisibility = UITitlebarTitleVisibilityHidden; self.window.windowScene.titlebar.toolbar = nil; #endif UIStoryboard *sb = [UIStoryboard storyboardWithName:@"LaunchScreen" bundle:nil]; UIViewController *vc = [sb instantiateInitialViewController]; rootView.loadingView = vc.view; return YES; } /// 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]; #ifdef RCT_NEW_ARCH_ENABLED initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]); #endif return initProps; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else return [CodePush bundleURL]; #endif } - (void)configureFirebase { #if DEBUG NSString *plistFileName = @"GoogleService-Info-develop"; #else NSString *plistFileName = @"GoogleService-Info"; #endif NSString *plistPath = [[NSBundle mainBundle] pathForResource:plistFileName ofType:@"plist"]; FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:plistPath]; [FIRApp configureWithOptions:options]; } //Called when a notification is delivered to a foreground app. -(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge); } // - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { // [[RNFirebaseNotifications instance] didReceiveLocalNotification:notification]; // } - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { [RNCPushNotificationIOS didReceiveNotificationResponse:response]; } #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 ```


Environment

Click To Expand

**`react-native info` output:** ``` System: OS: macOS 12.3.1 CPU: (10) x64 Apple M1 Max Memory: 162.46 MB / 64.00 GB Shell: 3.2.57 - /bin/bash Binaries: Node: 16.17.0 - /var/folders/rv/gqqc1y710gs7kybklq1tvfbc0000gp/T/yarn--1669036692568-0.8333042710226182/node Yarn: 1.22.10 - /var/folders/rv/gqqc1y710gs7kybklq1tvfbc0000gp/T/yarn--1669036692568-0.8333042710226182/yarn npm: 8.15.0 - ~/.nvm/versions/node/v16.17.0/bin/npm Watchman: 2022.08.22.00 - /usr/local/bin/watchman Managers: CocoaPods: 1.11.3 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: DriverKit 21.4, iOS 15.5, macOS 12.3, tvOS 15.4, watchOS 8.5 Android SDK: Not Found IDEs: Android Studio: 2021.3 AI-213.7172.25.2113.9123335 Xcode: 13.4.1/13F100 - /usr/bin/xcodebuild Languages: Java: Not Found npmPackages: @react-native-community/cli: Not Found react: 18.0.0 => 18.0.0 react-native: 0.69.7 => 0.69.7 react-native-macos: Not Found ``` - **Platform that you're experiencing the issue on**: - [ ] iOS - [ ] Android - [X] MacCatalyst - [ ] **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:** - latest: 16.4.5 (but persistent issue from previous versions when following documentation. You can remove the Push Notifications and then use ReCaptcha but you get into an endless loop that doesn't always work on Mac) - **`Firebase` module(s) you're using that has the issue:** - Auth - **Are you using `TypeScript`?** - Yes


@mikehardy if you have any ideas, we would appreciate it!

mikehardy commented 1 year ago
    # TODO: Remove Firebase lines once `pod install` works without them
    pod 'Firebase', :modular_headers => true
    pod 'FirebaseCore', :modular_headers => true
    pod 'GoogleUtilities', :modular_headers => true
    $RNFirebaseAsStaticFramework = true

This is not a valid configuration, you need to use use_frameworks! :linkage => :static, remove these headers workarounds, and disable flipper. Things should build then, although you've deleted big chunks of your package.json so I don't have enough data to help there. We strictly require use_frameworks though.

For your specific problem, it appears to be some sort of project configuration issue? Based on cursory glance through web search, most likely something about APNS certificates

https://search.brave.com/search?q=%5Bauth%2Finvalid-app-credential%5D+Token+mismatch+firebase+auth&source=desktop

https://stackoverflow.com/questions/74340881/invalid-app-credential-token-mismatch-in-flutter-firebase-auth https://github.com/invertase/react-native-firebase/issues/3183 https://github.com/firebase/firebase-ios-sdk/issues/1896#issuecomment-428084090

ZComwiz commented 1 year ago

@mikehardy currently we can build ok actually. Why should we disable Flipper? What debugging tools do you recommend instead?

ZComwiz commented 1 year ago

@mikehardy update on the issue:

I added this protocol override to force an unknown APNS token and also set the entitlement file as suggested here but it still does not alleviate the Token Mismatch error on Mac Catalyst.

-(void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
  [[FIRAuth auth] setAPNSToken: deviceToken type: FIRAuthAPNSTokenTypeUnknown];

}
mikehardy commented 1 year ago

Flipper does not support use_frameworks yet. You might try the JSI interface on hermes (if you use it) via chrome's dev tools Alternatively I recognize it's a different style but I use log statements personally.

I'm sorry about the macCatalyst issue but this will in the end be project config issue I believe, of some sort

ZComwiz commented 1 year ago

@mikehardy Have you attempted logging in on MacCatalyst with Phone Auth on a test project? It works fine on iOS and Android.

In Firebase console > Cloud Messaging: we use the APNs Authentication Key. Not the APNs Certificates. Does it matter?

[❌] Perhaps MacCatalyst does something to the bundleID covertly like appending a prefix which Firebase Console doesn't see? β€” [Confirmed just now that this is not the case]

mikehardy commented 1 year ago

I have not personally used macCatalyst - I've worked with the react-native team (and the code here) to make sure it builds with use_frameworks (quite an odyssey, actually over the 0.65-67 react-native release cycles...) and my build script checks it, but I've not personally done it, my work apps aren't configured for macCatalyst uunfortunately

Auth is very difficult to set up for demonstrations because there aren't any good ways to perform the necessary configuration steps without leaking credentials or needing to share some secret, which doesn't work in a reproducible demo setup. So I'm not sure how to set up a reproducible demo here to dig further.

I'm not sure how useful this is, but you might try going down a level and checking firebase-ios-sdk directly via their auth quickstart to see if it functions https://github.com/firebase/quickstart-ios/tree/master/authentication

ZComwiz commented 1 year ago

@mikehardy after many breakpoints (including some on my own sanity), we determined that push notifications on Mac Catalyst need a secondary entitlement in addition to APS Environment: APS Environment (macOS) com.apple.developer.aps-environment in additon to aps-environment.

Developers should set both key values to 'unknown' for Apple to determine at runtime which key is appropriate (production or sandbox). Without com.apple.developer.aps-environment, aps-environment was not being respected on MacCatalyst and thus when you develop locally sandbox mode is never set to Firebase settings. One must manually override it to [[FIRAuth auth] setAPNSToken: deviceToken type: FIRAuthAPNSTokenTypeSandbox]; if you do not set com.apple.developer.aps-environment in info.plist on Mac.

Bottomline:

Firebase and Firebase React Native Auth should update documentation for Mac Catalyst to tell developers to add com.apple.developer.aps-environment : unknown to info.plist

mikehardy commented 1 year ago

Fascinating - that does look frustratingly subtle to discover.

Is the full set of actions just to add that key to Info.plist? If so, that's a pretty easy doc update, but I want to confirm.

I have no idea where in the docs this should go though - I guess here for us? https://rnfirebase.io/auth/phone-auth#ios-setup

Not sure where in the upstream docs, I don't see a good spot here :thinking: - maybe here? https://firebase.google.com/docs/auth/ios/phone-auth#start-receiving-silent-notifications

ZComwiz commented 1 year ago

Enabling push notifications automatically adds aps-environment to the entitlement file. MacCatalyst developers must manually go in and add com.apple.developer.aps-environment

No other action is needed, but our process to debug almost had us write conditional Macros to check if we are building to Archive Release Mode vs locally (debug and release tracks are not enough to determine the correct tokenβ€” App Store Release requires Production and any local builds to your devices from your computer require sandbox). However, by setting both of the aforementioned APS keys to unknown, we get the expected results that the device will self determine the correct key on Mac and iOS and set the Firebase key appropriately which is a much less complex solution.

https://rnfirebase.io/auth/phone-auth#ios-setup is appropriate. or add phone-auth#maccatalyst-setup as an H1. Up to you :)

mikehardy commented 1 year ago

Wait :-) please let me leave this one open to track the documentation update need

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.