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

🔥 [🐛] Account verification email fails to send with dynamic link on Android #5446

Closed guytepper closed 2 years ago

guytepper commented 3 years ago

Issue

Hi there! 😊 I'm using the auth library to send verification mail to a new user. I've turned on the option to send a dynamic link to open the app for continuing the sign up process.

userCredential.user.sendEmailVerification({
  handleCodeInApp: true,
  url: "https://my-app-name.page.link/email-verified",
})

On iOS it works fine - Firebase sends the email with a dynamic link prepended to the URL in the email (https://my-app-name.page.link?link=https://full-verification-link) that opens the app.

However, when I call the method on Android, the mail being sent without the dynamic link prepended (https://full-verification-link), so it opens the browser instead of the app.

When I create dynamic links they open the Android app, so it's just the verification email that doesn't being sent correctly. Any idea why it happens?


Project Files

Javascript

Click To Expand

#### `package.json`: ```json "dependencies": { "@react-native-firebase/app": "^12.0.0", "@react-native-firebase/auth": "^12.0.0", "@react-native-firebase/dynamic-links": "^12.0.0", "react": "17.0.1", "react-native": "0.64.1", ... } ``` #### `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 require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' require_relative '../node_modules/react-native-unimodules/cocoapods.rb' platform :ios, '12.0' target [app-ame] do use_unimodules! config = use_native_modules! permissions_path = '../node_modules/react-native-permissions/ios' use_react_native!( :path => config[:reactNativePath], # to enable hermes on iOS, change `false` to `true` and then install pods :hermes_enabled => true ) end ``` #### `AppDelegate.m`: ```objc #import "AppDelegate.h" #import #import #import #import #import #import #import #ifdef FB_SONARKIT_ENABLED #import #import #import #import #import #import static void InitializeFlipper(UIApplication *application) { FlipperClient *client = [FlipperClient sharedClient]; SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; [client addPlugin:[FlipperKitReactPlugin new]]; [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; [client start]; } #endif @interface AppDelegate () @property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { if ([FIRApp defaultApp] == nil) { [FIRApp configure]; } #ifdef FB_SONARKIT_ENABLED InitializeFlipper(application); #endif self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]]; RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"app-name" initialProperties:nil]; 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]; return YES; } - (NSArray> *)extraModulesForBridge:(RCTBridge *)bridge { NSArray> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge]; return extraModules; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } @end ```


Android

Click To Expand

#### Have you converted to AndroidX? - [x] my application is an AndroidX application? - [x] I am using `android/gradle.settings` `jetifier=true` for Android compatibility? - [x] I am using the NPM package `jetifier` for react-native compatibility? #### `android/build.gradle`: ```groovy buildscript { ext { buildToolsVersion = "29.0.3" minSdkVersion = 24 compileSdkVersion = 29 targetSdkVersion = 29 ndkVersion = "20.1.5948944" kotlinVersion = '1.5.0' firebaseIidVersion = "19.0.1" } repositories { google() jcenter() } dependencies { classpath("com.android.tools.build:gradle:4.1.0") classpath 'com.google.gms:google-services:4.3.8' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.0" } } allprojects { repositories { mavenLocal() maven { // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm url("$rootDir/../node_modules/react-native/android") } maven { // Android JSC is installed from npm url("$rootDir/../node_modules/jsc-android/dist") } google() jcenter() maven { url 'https://www.jitpack.io' } } } ``` #### `android/app/build.gradle`: ```groovy apply plugin: "com.android.application" apply plugin: 'com.google.gms.google-services' apply from: '../../node_modules/react-native-unimodules/gradle.groovy' import com.android.build.OutputFile project.ext.react = [ enableHermes: true, // clean and rebuild if changing ] apply from: "../../node_modules/react-native/react.gradle" def enableSeparateBuildPerCPUArchitecture = false def enableProguardInReleaseBuilds = false def jscFlavor = 'org.webkit:android-jsc:+' def enableHermes = project.ext.react.get("enableHermes", false); android { ndkVersion rootProject.ext.ndkVersion compileSdkVersion rootProject.ext.compileSdkVersion compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } dexOptions { javaMaxHeapSize "4g" } defaultConfig { applicationId "app-id" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" multiDexEnabled true } splits { abi { reset() enable enableSeparateBuildPerCPUArchitecture universalApk false // If true, also generate a universal APK include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" } } dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "com.facebook.react:react-native:+" // From node_modules implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" addUnimodulesDependencies() debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { exclude group:'com.facebook.fbjni' } debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { exclude group:'com.facebook.flipper' exclude group:'com.squareup.okhttp3', module:'okhttp' } debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { exclude group:'com.facebook.flipper' } if (enableHermes) { def hermesPath = "../../node_modules/hermes-engine/android/"; debugImplementation files(hermesPath + "hermes-debug.aar") releaseImplementation files(hermesPath + "hermes-release.aar") } else { implementation jscFlavor } } task copyDownloadableDepsToLibs(type: Copy) { from configurations.compile into 'libs' } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) ``` #### `android/settings.gradle`: ```groovy apply from: '../node_modules/react-native-unimodules/gradle.groovy'; includeUnimodulesProjects() apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) include ':app' ``` #### `MainApplication.java`: ```java package com.app-name; import com.app-name.generated.BasePackageList; import android.app.Application; import android.content.Context; import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.soloader.SoLoader; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Arrays; import org.unimodules.adapters.react.ModuleRegistryAdapter; import org.unimodules.adapters.react.ReactModuleRegistryProvider; import org.unimodules.core.interfaces.SingletonModule; public class MainApplication extends Application implements ReactApplication { private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(new BasePackageList().getPackageList(), null); private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List packages = new PackageList(this).getPackages(); // Packages that cannot be autolinked yet can be added manually here, for example: // packages.add(new MyReactNativePackage()); // Add unimodules List unimodules = Arrays.asList( new ModuleRegistryAdapter(mModuleRegistryProvider) ); packages.addAll(unimodules); return packages; } @Override protected String getJSMainModuleName() { return "index"; } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); SoLoader.init(this, /* native exopackage */ false); initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); } /** * Loads Flipper in React Native templates. Call this in the onCreate method with something like * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); * * @param context * @param reactInstanceManager */ private static void initializeFlipper( Context context, ReactInstanceManager reactInstanceManager) { if (BuildConfig.DEBUG) { try { /* We use reflection here to pick up the class that initializes Flipper, since Flipper library is not available in release mode */ Class aClass = Class.forName("com.app-name.ReactNativeFlipper"); aClass .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) .invoke(null, context, reactInstanceManager); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } ``` #### `AndroidManifest.xml`: ```xml ```


Environment

Click To Expand

**`react-native info` output:** ``` System: OS: macOS 11.4 Shell: 5.8 - /bin/zsh Binaries: Node: 15.2.1 - /usr/local/bin/node Yarn: 1.22.10 - /usr/local/bin/yarn npm: 7.14.0 - /usr/local/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman Managers: CocoaPods: 1.10.1 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: iOS 14.5, DriverKit 20.4, macOS 11.3, tvOS 14.5, watchOS 7.4 Android SDK: API Levels: 29, 30 Build Tools: 28.0.3, 29.0.2, 30.0.2 System Images: android-30 | Google APIs Intel x86 Atom Android NDK: Not Found IDEs: Android Studio: 4.1 AI-201.8743.12.41.7199119 Xcode: 12.5/12E262 - /usr/bin/xcodebuild Languages: Java: 1.8.0_275 - /usr/bin/javac npmPackages: @react-native-community/cli: Not Found react: 17.0.1 => 17.0.1 react-native: 0.64.1 => 0.64.1 react-native-macos: Not Found npmGlobalPackages: ``` - **Platform that you're experiencing the issue on**: - [ ] iOS - [x] 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:** - `12.0.0` - **`Firebase` module(s) you're using that has the issue:** - `@react-native-firebase/auth` - **Are you using `TypeScript`?** - Yes, `4.2.3`

mikehardy commented 3 years ago

:thinking:

    "@react-native-firebase/app": "^12.0.0",
    "@react-native-firebase/auth": "^12.0.0",
    "@react-native-firebase/dynamic-links": "^12.0.0",
    "react": "17.0.1",
    "react-native": "0.64.1",

we're on 12.1.0 here and react-native is 0.64.2, not sure that's important for your issue, but current is better when it comes to reproducing and triaging problems.

from gradle files:

   firebaseIidVersion = "19.0.1"

That's concerning, IID is no longer a published library from the firebase set, it's fully deprecated and removed. What do you have that is still using it? You should see if you can remove that as it might cause unsupported / old library resolution. No modules anywhere should be referencing it any more

Not sure that has bearing on the problem, but worth mentioning.

I'm not sure what is going wrong but you could help drive this closer to resolution by pulling the test app and running it, changing the it here to it.only to focus in on exactly the moment where we test this

https://github.com/invertase/react-native-firebase/blob/46556de7ce17ef1d55b52222a907732353addde6/packages/auth/e2e/emailLink.e2e.js#L31-L53

We intend to exercise the functionality right there. Is it not working for some reason? Is the test not actually exercising it correctly? Or is it working and there is a project-specific difference in your project config or otherwise?

guytepper commented 3 years ago

I've removed the firebaseIidVersion and updated the firebase libs - didn't help 😕

I'll give the tests a closer look later this week - how should I approach checking the email sent? Should I replace the address in the test with mine?

Btw, not sure if it matters but the method tested there is sendSignInLinkToEmail, while I'm using sendEmailVerification

mikehardy commented 3 years ago

I've removed the firebaseIidVersion and updated the firebase libs - didn't help confused

That was quite a long shot, but better that we have current versions so we know we're not chasing shadows, thanks for trying

I'll give the tests a closer look later this week - how should I approach checking the email sent? Should I replace the address in the test with mine?

I'm not entirely sure - the auth tests interact with the auth emulator, so you'll have lots of local inspection abilities and actual email will not go out I think, though in the test harness you may also disable the auth emulator and reconfigure it for your project if you like in order to verify different scenarios. This is more to get a workbench of tools laid out for inspection - what the best path is after inspection starts? not exactly sure...

Btw, not sure if it matters but the method tested there is sendSignInLinkToEmail, while I'm using sendEmailVerification

Oh! Sorry about that. That matters a lot. We test sendEmailVerification quite a bit but I don't believe we exercise the custom domain and handle-in-app combination https://github.com/invertase/react-native-firebase/blob/46556de7ce/packages/auth/e2e/user.e2e.js - you will want to focus there then. May pay to add a new test that is your exact case in order to have your specific issue under your scope

stale[bot] commented 3 years 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 the community's attention?

This issue will be closed in 15 days if no further activity occurs. Thank you for your contributions.