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

🔥 [🐛] auth().verifyPhoneNumber() takes >20s to resolve on Android devices #7501

Closed loganwishartcraig closed 6 months ago

loganwishartcraig commented 8 months ago

Issue

We're seeing a significant percentage of Android users for whom the auth().verifyPhoneNumber(...) Promise is taking >20s to resolve (~50%)

image

It appears the authorization code is sent relatively quickly (~1-2s), but the Promise itself doesn't resolve for quite some time after that.

We're not seeing any errors, just slowness. Not seeing slowness on iOS. We're leveraging the Play Integrity API for device verification. I'm able to reproduce locally. Setting auth().settings.appVerificationDisabledForTesting = true seemed to have no effect. auth().signInWithPhoneNumber() seems to resolve very quickly.

Is anyone else seeing this? Any clue what we might try? Happy to provide any further details/troubleshooting!


Project Files

Javascript

Click To Expand

#### `package.json`: ```json { "version": "0.0.1", "private": true, "dependencies": { "@amplitude/react-native": "2.15.0", "@gorhom/bottom-sheet": "4.5.1", "@invertase/react-native-apple-authentication": "2.1.2", "@notifee/react-native": "7.8.0", "@react-native-async-storage/async-storage": "1.13.2", "@react-native-camera-roll/camera-roll": "5.0.3", "@react-native-clipboard/clipboard": "1.7.0", "@react-native-community/datetimepicker": "3.4.0", "@react-native-community/hooks": "2.8.1", "@react-native-community/netinfo": "11.1.0", "@react-native-community/push-notification-ios": "1.8.0", "@react-native-firebase/app": "18.7.1", "@react-native-firebase/app-check": "18.7.1", "@react-native-firebase/auth": "18.7.1", "@react-native-firebase/database": "18.7.1", "@react-native-firebase/dynamic-links": "18.7.1", "@react-native-firebase/functions": "18.7.1", "@react-native-firebase/installations": "18.7.1", "@react-native-firebase/messaging": "18.7.1", "@react-native-firebase/remote-config": "18.7.1", "@react-native-firebase/storage": "18.7.1", "@react-native-google-signin/google-signin": "10.0.1", "@react-native-masked-view/masked-view": "0.2.9", "@react-navigation/bottom-tabs": "^6.3.1", "@react-navigation/drawer": "6.4.1", "@react-navigation/native": "^6.0.10", "@react-navigation/stack": "^6.2.1", "@reduxjs/toolkit": "1.3.4", "@ronradtke/react-native-markdown-display": "8.0.0", "@sentry/react-native": "5.15.0", "@shopify/flash-list": "1.4.3", "@stream-io/flat-list-mvcp": "0.10.3", "@stripe/stripe-react-native": "0.35.0", "@tanstack/react-query": "4.14.1", "@xstate/react": "0.8.1", "date-fns": "^2.28.0", "expo": "^49.0.0", "expo-av": "~13.4.1", "expo-calendar": "~11.3.2", "expo-contacts": "~12.2.0", "expo-file-system": "~15.4.4", "expo-image": "~1.5.1", "expo-image-manipulator": "~11.5.0", "expo-image-picker": "~14.5.0", "expo-localization": "~14.3.0", "expo-location": "~16.1.0", "expo-media-library": "~15.6.0", "expo-video-thumbnails": "~7.4.0", "i18next": "22.4.11", "jotai": "2.1.0", "lucide-react-native": "0.292.0", "mixpanel-react-native": "2.2.0", "path-to-regexp": "6.2.1", "react": "18.2.0", "react-i18next": "12.2.0", "react-native": "0.72.7", "react-native-add-calendar-event": "4.0.0", "react-native-bootsplash": "4.3.2", "react-native-calendars": "1.1293.0", "react-native-code-push": "8.1.0", "react-native-confetti-cannon": "1.5.0", "react-native-config": "1.5.0", "react-native-console-time-polyfill": "1.2.1", "react-native-device-info": "10.6.0", "react-native-dropdownalert": "3.9.2", "react-native-error-boundary": "1.1.16", "react-native-exception-handler": "2.10.10", "react-native-gesture-handler": "~2.12.0", "react-native-get-random-values": "1.4.0", "react-native-hyperlink": "0.0.21", "react-native-image-crop-picker": "0.37.3", "react-native-image-header-scroll-view": "0.10.3", "react-native-image-zoom-viewer": "3.0.1", "react-native-in-app-review": "^4.1.1", "react-native-input-scroll-view": "1.11.0", "react-native-launch-arguments": "3.1.2", "react-native-linear-gradient": "2.8.3", "react-native-material-menu": "1.1.2", "react-native-mmkv": "2.11.0", "react-native-modal": "11.5.3", "react-native-modal-overlay": "1.3.1", "react-native-paper": "5.11.3", "react-native-permissions": "3.10.1", "react-native-progress": "^5.0.0", "react-native-qrcode-svg": "6.1.1", "react-native-reanimated": "3.5.4", "react-native-reanimated-carousel": "3.1.5", "react-native-safe-area-context": "^4.7.4", "react-native-screens": "^3.27.0", "react-native-share": "8.2.2", "react-native-simple-radio-button": "2.7.3", "react-native-snow": "0.2.0", "react-native-svg": "13.14.0", "react-native-tab-view": "3.1.1", "react-native-url-polyfill": "1.3.0", "react-native-v8": "0.60.5-patch.0", "react-native-vector-icons": "9.2.0", "react-redux": "7.2.4", "redux-batched-subscribe": "0.1.6", "redux-catch": "1.3.1", "redux-logger": "^3.0.6", "redux-persist": "6.0.0", "redux-thunk": "2.3.0", "reselect": "^4.0.0", "rn-fetch-blob": "0.10.16", "seedrandom": "^3.0.5", "semver": "^7.3.7", "shorthash": "0.0.2", "typesafe-actions": "5.1.0", "utility-types": "3.10.0", "validator": "^13.7.0" }, "devDependencies": { "@babel/core": "^7.20.0", "@babel/preset-env": "^7.20.0", "@babel/runtime": "^7.20.0", "@react-native/eslint-config": "^0.72.2", "@react-native/metro-config": "^0.72.11", "@storybook/react-native": "5.3.25", "@testing-library/jest-native": "5.4.3", "@testing-library/react-hooks": "8.0.1", "@testing-library/react-native": "12.4.0", "@tsconfig/react-native": "^3.0.0", "@types/chai": "4.1.7", "@types/node-fetch": "2.5.8", "@types/react": "^18.0.28", "@types/react-native-vector-icons": "6.4.6", "@types/react-test-renderer": "^18.0.6", "@types/redux-persist": "4.3.1", "@types/seedrandom": "2.4.28", "@types/semver": "^7.3.9", "@types/sinon": "^10.0.11", "@welldone-software/why-did-you-render": "7.0.1", "babel-jest": "^29.2.1", "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.2.0", "chai-subset": "1.6.0", "detox": "^20.11.2", "jest-expo": "48.0.2", "jetifier": "1.6.6", "metro-react-native-babel-preset": "0.76.8", "react-native-storybook-loader": "1.8.1", "react-test-renderer": "18.2.0", "sinon": "^9.2.4", "yarn-check-webpack-plugin": "1.2.0" }, "peerDependencies": { "jest": "*" }, "config": { "react-native-storybook-loader": { "searchDir": [ "./src", "../components/src" ], "pattern": "**/*.stories.tsx", "outputFile": "./storybook/storyLoader.js" } } } ``` #### `firebase.json` for react-native-firebase v6: ```json { "database": { "predeploy": [ "yarn bolt && npx firebase emulators:exec --only database \"yarn firebase test\"" ], "rules": "packages/firebase/database.rules.json" }, "functions": { "predeploy": ["./scripts/pre-deploy-cloud-functions.sh"], "source": "webpack/cloud-functions" }, "storage": { "predeploy": ["yarn bolt"], "rules": "storage.rules", "source": "packages/firebase" }, "hosting": [ { "target": "storybook", "public": "packages/web/storybook-static" }, { "target": "app-alpha", "public": "packages/web/build", "ignore": ["**/.*"], "rewrites": [ { "source": "**", "destination": "/index.html" } ] }, { "target": "app-beta", "public": "packages/web/build", "ignore": ["**/.*"], "rewrites": [ { "source": "**", "destination": "/index.html" } ] }, { "target": "app-demo", "public": "packages/web/build", "ignore": ["**/.*"], "rewrites": [ { "source": "**", "destination": "/index.html" } ] }, { "target": "app-prod", "public": "packages/web/build", "ignore": ["**/.*"], { "source": "**", "destination": "/index.html" } ] }, { "target": "redirect-prod", "source": "packages/redirect", "ignore": ["**/.*"], "frameworksBackend": { "region": "us-central1" } }, { "target": "auth-beta", "public": "packages/web/build", "ignore": ["**/.*"], "rewrites": [ { "source": "**", "destination": "/index.html" } ] }, { "target": "auth-prod", "public": "packages/web/build", "ignore": ["**/.*"], "rewrites": [ { "source": "**", "destination": "/index.html" } ] } ], "emulators": { "auth": { "host": "0.0.0.0", "port": 9099 }, "database": { "host": "0.0.0.0", "port": 9000 }, "functions": { "host": "0.0.0.0", "port": 5001 }, "hosting": { "host": "0.0.0.0", "port": 3000 }, "ui": { "enabled": true } } } ```

iOS

Click To Expand

#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like: ```ruby // NA ``` #### `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? - [x] I am using the NPM package `jetifier` for react-native compatibility? (I think) #### `android/build.gradle`: ```groovy // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext { buildToolsVersion = "33.0.0" minSdkVersion = 23 compileSdkVersion = 33 targetSdkVersion = 33 // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. ndkVersion = "23.1.7779620" // Added for Detox, react-native-gesture-handler, @stripe/stripe-react-native kotlinVersion = '1.8.22' } repositories { google() mavenCentral() } dependencies { classpath("com.android.tools.build:gradle") classpath("com.facebook.react:react-native-gradle-plugin") // Added for react-native-firebase classpath 'com.google.gms:google-services:4.4.0' // Added for Detox. classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath("io.sentry:sentry-android-gradle-plugin:3.14.0") } } ``` #### `android/app/build.gradle`: ```groovy // ... dependencies { implementation 'androidx.multidex:multidex:2.0.1' implementation "androidx.core:core-splashscreen:1.0.0" // NB: added to potentially fix an Android crash. See // https://stackoverflow.com/questions/76410642/viewtreelifecycleowner-not-found-when-dismissing-from-bottomsheetdialogfragment implementation "androidx.recyclerview:recyclerview:1.3.2" // The version of react-native is set by the React Native Gradle Plugin implementation("com.facebook.react:react-android") debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { exclude group:'com.squareup.okhttp3', module:'okhttp' } debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") if (hermesEnabled.toBoolean()) { implementation("com.facebook.react:hermes-android") } else { implementation jscFlavor } // For animated GIF support implementation 'com.facebook.fresco:animated-gif:2.6.0' // For WebP support, including animated WebP implementation 'com.facebook.fresco:animated-webp:2.6.0' implementation 'com.facebook.fresco:webpsupport:2.6.0' // NB: do not try to migrate to AndroidX yet... too many react-native modules // still have hard dependencies on legacy Android libraries and this causes painful // build issues. E.g., disabling AndroidX / Jetifier on a per node-module basis. // //implementation 'androidx.appcompat:appcompat:1.0.2' implementation "com.google.android.gms:play-services-auth:16.0.1" implementation "com.google.android.material:material:1.6.0" implementation fileTree(dir: "libs", include: ["*.jar"]) implementation 'me.leolin:ShortcutBadger:1.1.21@aar' // <-- Added to use badge on Android. // Add the dependencies for the App Check libraries // // Import the BoM (Bill of Materials) for the Firebase platform. When using // BoM, you don't use specific versions of Firebase dependencies. BoM will // determine all specific versions and they will be known to be compatible // with one another. To learn more, see // https://firebase.google.com/docs/android/learn-more#bom implementation(platform("com.google.firebase:firebase-bom:32.6.0")) implementation("com.google.firebase:firebase-auth") implementation("com.google.firebase:firebase-appcheck-playintegrity") androidTestImplementation('com.wix:detox:+') } ``` #### `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 14.1 CPU: (10) arm64 Apple M1 Max Memory: 3.26 GB / 64.00 GB Shell: version: "5.9" path: /bin/zsh Binaries: Node: version: 18.18.0 path: ~/work/demo/.devbox/nix/profile/default/bin/node Yarn: version: 1.22.19 path: ~/work/demo/.devbox/nix/profile/default/bin/yarn npm: version: 9.8.1 path: ~/work/demo/.devbox/nix/profile/default/bin/npm Watchman: version: 2023.08.14.00 path: /Users/logan/work/demo/.devbox/nix/profile/default/bin/watchman Managers: CocoaPods: version: 1.12.1 path: /Users/logan/work/demo/.devbox/nix/profile/default/bin/pod SDKs: iOS SDK: Platforms: - DriverKit 23.0 - iOS 17.0 - macOS 14.0 - tvOS 17.0 - watchOS 10.0 Android SDK: Not Found IDEs: Android Studio: 2023.1 AI-231.9392.1.2311.11076708 Xcode: version: 15.0.1/15A507 path: /usr/bin/xcodebuild Languages: Java: version: 11.0.11 path: /Users/logan/work/demo/.devbox/nix/profile/default/bin/javac Ruby: version: 2.7.5 path: /Users/logan/.rbenv/shims/ruby npmPackages: "@react-native-community/cli": Not Found react: Not Found react-native: installed: 0.72.7 wanted: 0.72.7 react-native-macos: Not Found npmGlobalPackages: "*react-native*": Not Found Android: hermesEnabled: true newArchEnabled: false iOS: hermesEnabled: true newArchEnabled: false ``` - **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:** 18.7.1 - `e.g. 5.4.3` - **`Firebase` module(s) you're using that has the issue:** Authentication - `e.g. Instance ID` - **Are you using `TypeScript`?** Y 4.9.5 - `Y/N` & `VERSION`


puls commented 8 months ago

We also saw this; the culprit is that the promise doesn't resolve on Android until after a 20 second timeout in order to let the system try and auto-retrieve the code.

You can listen to the state_changed event on the PhoneAuthListener that you get back from verifyPhoneNumber instead of treating it like a promise to get a more immediate indication of when the code was sent.

github-actions[bot] commented 7 months 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.

gabriel-moresco commented 5 months ago

@loganwishartcraig how did you solve that?

loganwishartcraig commented 5 months ago

@gabriel-moresco We haven't really. This is our current usage, but we're still seeing a bunch of timeout errors on android.

    const confirmation = await new Promise<FirebaseAuthTypes.PhoneAuthSnapshot>(
      (res, rej) => {
        auth()
          .verifyPhoneNumber(phoneNumber, 1, true)
          .on('state_changed', evt => {
            if (evt.state === 'error' || evt.state === 'timeout') {
              rej(evt)
            } else if (evt.state === 'sent' || evt.state === 'verified') {
              res(evt)
            }
          })
      }
    )