React-native-firebase: ERROR_SESSION_EXPIRED: The SMS code has expired. Please re-send the verification code to try again. (iOS)

Created on 1 Jan 2020  ·  25Comments  ·  Source: invertase/react-native-firebase


Issue



I am getting the following on a live app (on the App Store).
The phone verification code gets sent just fine, but seems to be immediately expired upon trying to type it in.
This only appears to be happening only for new users and only in production on the App Store.

{
  "code": "auth/unknown",
  "error_name": "ERROR_SESSION_EXPIRED",
  "NSLocalizedDescription: "The SMS code has expired. Please re-send the verification code to try again."
}
  continue = async (code) => {
    const value = code || this.field.current.state.codeValue;
    try {
      await this.state.confirmResult.confirm(value);
    } catch (err) {
      console.log(err);
      alert('Wrong code ❌');
      this.clearCode();
    }
  }

This is strange because I just carried out a beta test on TestFlight and there were no issues using phone auth for 1000+ users, it worked perfectly fine. Just my luck that as soon as the app gets distributed that new users can't join anymore.

Test Numbers (added in Firebase UI): WORKING
Simulator: WORKING
TestFlight: WORKING
Live: WORKING for existing users who already successfully phone auth'd at least once i.e. former beta testers, but FAILING for new users trying to phone auth for the first time i.e. new people trying to register :C


Project Files






Javascript

Click To Expand

#### `package.json`:

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "devDependencies": {
    "babel-loader": "^7.1.2",
    "babel-plugin-expo-web": "^0.0.5",
    "babel-plugin-react-native-web": "^0.4.0",
    "babel-plugin-transform-decorators-legacy": "^1.3.4",
    "babel-plugin-transform-imports": "^1.4.1",
    "babel-plugin-transform-runtime": "^6.23.0",
    "css-loader": "^0.28.7",
    "file-loader": "^1.1.7",
    "jest-expo": "~27.0.0",
    "react-native-scripts": "latest",
    "react-test-renderer": "16.3.1",
    "style-loader": "^0.19.0"
  },
  "main": "./node_modules/react-native-scripts/build/bin/crna-entry-web.js",
  "scripts": {
    "start": "expo start",
    "ios": "expo ios",
    "android": "expo android",
    "eject": "expo eject",
    "test": "jest",
    "web": "webpack-dev-server -d --config ./webpack.config.js  --inline --hot --colors --content-base public/ --history-api-fallback",
    "build": "NODE_ENV=production webpack -p --config ./webpack.config.js"
  },
  "jest": {
    "preset": "jest-expo"
  },
  "dependencies": {
    "@ptomasroos/react-native-multi-slider": "^0.0.14",
    "@react-native-community/async-storage": "^1.5.0",
    "apollo-boost": "^0.1.7",
    "apollo-cache-persist": "^0.1.1",
    "apollo-client": "2.4.2",
    "apollo-link-batch-http": "^1.2.8",
    "apollo-link-context": "^1.0.9",
    "apollo-link-http": "^1.5.4",
    "apollo-link-schema": "^1.1.0",
    "apollo-link-state": "^0.4.1",
    "apollo-link-ws": "^1.0.9",
    "apollo-utilities": "1.0.21",
    "axios": "^0.18.0",
    "babel-plugin-transform-remove-console": "^6.9.4",
    "expo": "30.0.0",
    "expo-web": "^0.0.12",
    "expokit": "1.7.1",
    "faker": "^4.1.0",
    "geolib": "^2.0.24",
    "graphql": "^0.13.2",
    "graphql-iso-date": "^3.5.0",
    "graphql-tag": "^2.9.2",
    "graphql-tools": "^3.0.2",
    "immutability-helper": "^3.0.0",
    "qs": "^6.7.0",
    "react": "16.3.1",
    "react-apollo": "^2.1.4",
    "react-dom": "16.0.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-30.0.0.tar.gz",
    "react-native-animatable": "^1.3.1",
    "react-native-check-box": "^2.1.0",
    "react-native-circular-progress": "^1.1.0",
    "react-native-collapsible": "^0.11.3",
    "react-native-confirmation-code-field": "3.7.0",
    "react-native-country-picker-modal": "^0.8.0",
    "react-native-datepicker": "^1.7.2",
    "react-native-deck-swiper": "^1.5.15",
    "react-native-device-info": "^1.6.1",
    "react-native-dropdownalert": "^4.1.0",
    "react-native-fast-image": "^5.1.1",
    "react-native-fbsdk": "^0.8.0",
    "react-native-firebase": "^5.0.0-rc4",
    "react-native-gifted-chat": "^0.4.3",
    "react-native-haptic-feedback": "^1.6.0",
    "react-native-hyperlink": "^0.0.14",
    "react-native-iap": "2.3.26",
    "react-native-iphone-x-helper": "^1.2.0",
    "react-native-loading-spinner-overlay": "^1.0.1",
    "react-native-modal": "^11.0.1",
    "react-native-onboarding-swiper": "^1.0.0",
    "react-native-parallax-scroll-view": "^0.21.3",
    "react-native-phone-input": "^0.2.4",
    "react-native-simple-radio-button": "^2.7.3",
    "react-native-smart-corner-label": "^1.1.2",
    "react-native-smart-timer-enhance": "^1.0.3",
    "react-native-snap-carousel": "^3.8.1",
    "react-native-sortable-listview": "^0.2.8",
    "react-native-storage": "^1.0.1",
    "react-native-svg-animated-linear-gradient": "^0.1.9",
    "react-native-swipe-list-view": "^1.5.1",
    "react-native-swiper": "^1.5.13",
    "react-native-tab-view": "^1.3.2",
    "react-native-vector-icons": "^6.3.0",
    "react-native-web": "^0.7.3",
    "react-navigation": "^3.3.0",
    "react-navigation-fluid-transitions": "^0.2.74",
    "react-primitives": "^0.5.1",
    "rn-placeholder": "^2.0.0",
    "styled-components": "^3.2.6",
    "subscriptions-transport-ws": "^0.9.15",
    "uuid": "^3.3.2",
    "webpack": "^3.11.0",
    "webpack-dev-server": "2.9.4"
  }
}

#### `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:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'

target 'client' do
  pod 'ExpoKit',
    :git => "http://github.com/expo/expo.git",
    :tag => "ios/2.8.3",
    :subspecs => [
      "Core"
    ],
    :inhibit_warnings => true
  pod 'EXAdsAdMob',
    :path => "../node_modules/expo-ads-admob/ios"
  pod 'EXBarCodeScanner',
    :path => "../node_modules/expo-barcode-scanner/ios"
  pod 'EXBarCodeScannerInterface',
    :path => "../node_modules/expo-barcode-scanner-interface/ios"
  pod 'EXCamera',
    :path => "../node_modules/expo-camera/ios"
  pod 'EXCameraInterface',
    :path => "../node_modules/expo-camera-interface/ios"
  pod 'EXConstants',
    :path => "../node_modules/expo-constants/ios"
  pod 'EXConstantsInterface',
    :path => "../node_modules/expo-constants-interface/ios"
  pod 'EXContacts',
    :path => "../node_modules/expo-contacts/ios"
  pod 'EXCore',
    :path => "../node_modules/expo-core/ios"
  pod 'EXFaceDetectorInterface',
    :path => "../node_modules/expo-face-detector-interface/ios"
  pod 'EXFileSystem',
    :path => "../node_modules/expo-file-system/ios"
  pod 'EXFileSystemInterface',
    :path => "../node_modules/expo-file-system-interface/ios"
  pod 'EXFont',
    :path => "../node_modules/expo-font/ios"
  pod 'EXFontInterface',
    :path => "../node_modules/expo-font-interface/ios"
  pod 'EXGL',
    :path => "../node_modules/expo-gl/ios"
  pod 'EXGL-CPP',
    :path => "../node_modules/expo-gl-cpp/cpp"
  pod 'EXImageLoaderInterface',
    :path => "../node_modules/expo-image-loader-interface/ios"
  pod 'EXLocalAuthentication',
    :path => "../node_modules/expo-local-authentication/ios"
  pod 'EXLocation',
    :path => "../node_modules/expo-location/ios"
  pod 'EXMediaLibrary',
    :path => "../node_modules/expo-media-library/ios"
  pod 'EXPermissions',
    :path => "../node_modules/expo-permissions/ios"
  pod 'EXPermissionsInterface',
    :path => "../node_modules/expo-permissions-interface/ios"
  pod 'EXPrint',
    :path => "../node_modules/expo-print/ios"
  pod 'EXReactNativeAdapter',
    :path => "../node_modules/expo-react-native-adapter/ios"
  pod 'EXSegment',
    :path => "../node_modules/expo-analytics-segment/ios"
  pod 'EXSensors',
    :path => "../node_modules/expo-sensors/ios"
  pod 'EXSensorsInterface',
    :path => "../node_modules/expo-sensors-interface/ios"
  pod 'EXSMS',
    :path => "../node_modules/expo-sms/ios"

  pod 'React',
    :path => "../node_modules/react-native",
    :inhibit_warnings => true,
    :subspecs => [
      "Core",
      "ART",
      "RCTActionSheet",
      "RCTAnimation",
      "RCTCameraRoll",
      "RCTGeolocation",
      "RCTImage",
      "RCTNetwork",
      "RCTText",
      "RCTVibration",
      "RCTWebSocket",
      "DevSupport",
      "CxxBridge"
    ]
  pod 'yoga',
    :path => "../node_modules/react-native/ReactCommon/yoga",
    :inhibit_warnings => true
  pod 'DoubleConversion',
    :podspec => "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec",
    :inhibit_warnings => true
  pod 'Folly',
    :podspec => "../node_modules/react-native/third-party-podspecs/Folly.podspec",
    :inhibit_warnings => true
  pod 'glog',
    :podspec => "../node_modules/react-native/third-party-podspecs/glog.podspec",
    :inhibit_warnings => true

  pod 'Firebase/Core', '~> 5.3.0'
  pod 'Firebase/Auth', '~> 5.3.0'
  pod 'Firebase/Messaging', '~> 5.3.0'
  pod 'Firebase/Storage', '~> 5.3.0'
  pod 'Firebase/DynamicLinks', '~> 5.3.0'

  pod 'react-native-fast-image', :path => '../node_modules/react-native-fast-image'

  pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'

  pod 'react-native-haptic-feedback', :path => '../node_modules/react-native-haptic-feedback'

  pod 'react-native-fbsdk', :path => '../node_modules/react-native-fbsdk'

  pod 'RNDeviceInfo', :path => '../node_modules/react-native-device-info'

  pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-community/async-storage'

  post_install do |installer|
    installer.pods_project.main_group.tab_width = '2';
    installer.pods_project.main_group.indent_width = '2';

    installer.pod_targets.each do |target|

    if target.pod_name == 'ExpoKit'
      target.native_target.build_configurations.each do |config|
        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'EX_DETACHED=1'

        # needed for GoogleMaps 2.x
        config.build_settings['FRAMEWORK_SEARCH_PATHS'] ||= []
        config.build_settings['FRAMEWORK_SEARCH_PATHS'] << '${PODS_ROOT}/GoogleMaps/Base/Frameworks'
        config.build_settings['FRAMEWORK_SEARCH_PATHS'] << '${PODS_ROOT}/GoogleMaps/Maps/Frameworks'
      end
    end


    if ['Amplitude-iOS','Analytics','AppAuth','Branch','CocoaLumberjack','FBSDKCoreKit','FBSDKLoginKit','FBSDKShareKit','GPUImage','JKBigInteger2'].include? target.pod_name
      target.native_target.build_configurations.each do |config|
        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0'
      end
    end
    # Can't specify this in the React podspec because we need
    # to use those podspecs for detached projects which don't reference ExponentCPP.
    if target.pod_name.start_with?('React')
      target.native_target.build_configurations.each do |config|
        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0'
        config.build_settings['HEADER_SEARCH_PATHS'] ||= ['$(inherited)']
      end
    end
    # Build React Native with RCT_DEV enabled
    next unless target.pod_name == 'React'
    target.native_target.build_configurations.each do |config|
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'RCT_DEV=1'
    end

    end
  end
end

#### `AppDelegate.m`:
// Copyright 2015-present 650 Industries. All rights reserved.

#import "AppDelegate.h"
#import "ExpoKit.h"
#import "EXViewController.h"

#import <Firebase.h>
#import "RNFirebaseNotifications.h"
#import "RNFirebaseMessaging.h"
#import <FBSDKCoreKit/FBSDKCoreKit.h>

@interface AppDelegate ()

@property (nonatomic, strong) EXViewController *rootViewController;

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [FIRApp configure];
    [RNFirebaseNotifications configure];
    [[FBSDKApplicationDelegate sharedInstance] application:application
                             didFinishLaunchingWithOptions:launchOptions];

    _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    _window.backgroundColor = [UIColor whiteColor];
    [[ExpoKit sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
    _rootViewController = [ExpoKit sharedInstance].rootViewController;
    _window.rootViewController = _rootViewController;

    [_window makeKeyAndVisible];

    return YES;
}

#pragma mark - Handling URLs

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(nullable NSString *)sourceApplication annotation:(id)annotation
{
    return [[ExpoKit sharedInstance] application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray * _Nullable))restorationHandler
{
    return [[ExpoKit sharedInstance] application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
}

#pragma mark - Notifications

// - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token
// {
//     [[ExpoKit sharedInstance] application:application didRegisterForRemoteNotificationsWithDeviceToken:token];
// }

// - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)err
// {
//     [[ExpoKit sharedInstance] application:application didFailToRegisterForRemoteNotificationsWithError:err];
// }

// - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification
// {
//     [[ExpoKit sharedInstance] application:application didReceiveRemoteNotification:notification];
// }

// - (void)application:(UIApplication *)application didReceiveLocalNotification:(nonnull UILocalNotification *)notification
// {
//     [[ExpoKit sharedInstance] application:application didReceiveLocalNotification:notification];
// }

// - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(nonnull UIUserNotificationSettings *)notificationSettings
// {
//     [[ExpoKit sharedInstance] application:application didRegisterUserNotificationSettings:notificationSettings];
// }

#pragma mark - Firebase

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
  [[RNFirebaseNotifications instance] didReceiveLocalNotification:notification];
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
                                                       fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
  [[RNFirebaseNotifications instance] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
  [[RNFirebaseMessaging instance] didRegisterUserNotificationSettings:notificationSettings];
}

#pragma mark - FBSDK

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {

    BOOL handled = [[FBSDKApplicationDelegate sharedInstance] application:application
                                                                  openURL:url
                                                        sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
                                                               annotation:options[UIApplicationOpenURLOptionsAnnotationKey]
                    ];

    return handled;
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    [FBSDKAppEvents activateApp];
}

@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`:

// N/A
#### `android/app/build.gradle`:
// N/A
#### `android/settings.gradle`:
// N/A
#### `MainApplication.java`:
// N/A
#### `AndroidManifest.xml`:
<!-- N/A -->


Environment

Click To Expand

**`react-native info` output:**

react-native info
Scanning folders for symlinks in /Users/a/mission/client/node_modules (37ms)

Environment:
  OS: macOS 10.15.1
  Node: 8.9.4
  Yarn: 1.16.0
  npm: 6.9.0
  Watchman: 4.9.0
  Xcode: Xcode 11.2.1 Build version 11B500
  Android Studio: Not Found

Packages: (wanted => installed)
  react: 16.3.1 => 16.3.1
  react-native: https://github.com/expo/react-native/archive/sdk-30.0.0.tar.gz => 0.55.4
- **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:** - `5.0.0-rc4` - **`Firebase` module(s) you're using that has the issue:** - `Auth` - **Are you using `TypeScript`?** - `N`




Think react-native-firebase is great? Please consider supporting all of the project maintainers and contributors by donating via our Open Collective where all contributors can submit expenses. [Learn More]

Stale

All 25 comments

That doesn't sound like fun, but still, the template contains useful information, I can't even hazard a guess without that information

@mikehardy Thanks I updated with the template info.

I also found this https://github.com/invertase/react-native-firebase/issues/2541 by @jpudysz, but I am not entirely sure what the issue is. Should I be using signInWithCredential() instead of confirm()? I used confirm() because it was mentioned in the documentation and seemed to work just fine for my beta test.

great - happy to reopen then I'll look through it with time available

I think the issue you found, with @jpudysz hopefully offering some wisdom 🙏 is a good lead, as I have no actually implemented sign-in with SMS auth but I do SMS verification.

I do my entire thing like so in case it helps (not recommending you do it the same or not, it's just an implementation that seems to work)

  onEnterPhoneNumber = async (
    values: PhoneVerifyFormikValues,
    actions: FormikHelpers<PhoneVerifyFormikValues>
  ) => {
    const { phoneNumber } = values;
    console.log('in onEnterPhoneNumber, values: ' + phoneNumber);

    if (!firebase.auth().currentUser || firebase.auth().currentUser == null) {
      console.log('no current user, redirecting...');

      const dialog = (
        <SimpleDialog
          dialogId={_noUserDialogId}
          text={I18NService.translate('PhoneVerifyNotLoggedIn')}
          buttons={[
            {
              text: I18NService.translate('PhoneVerifyLogin'),
              onPress: () => {
                SimpleDialog.dismissAnimated(_noUserDialogId);
                this.props.navigation.navigate('Ingresa', {});
              },
            },
          ]}
        />
      );

      RX.Modal.show(dialog, _noUserDialogId);
      actions.setSubmitting(false);
    }

    Analytics.analyticsEvent('onEnterPhoneNumber', { phoneNumber: phoneNumber });

    firebase
      .auth()
      .verifyPhoneNumber(phoneNumber)
      .on(
        'state_changed',
        async phoneAuthSnapshot => {
          // How you handle these state events is entirely up to your ui flow and whether
          // you need to support both ios and android. In short: not all of them need to
          // be handled - it's entirely up to you, your ui and supported platforms.

          // E.g you could handle android specific events only here, and let the rest fall back
          // to the optionalErrorCb or optionalCompleteCb functions
          switch (phoneAuthSnapshot.state) {
            // ------------------------
            //  IOS AND ANDROID EVENTS
            // ------------------------
            case firebase.auth.PhoneAuthState.CODE_SENT: // or 'sent'
              console.log('code sent - verificationId: ' + phoneAuthSnapshot.verificationId);
              // on ios this is the final phone auth state event you'd receive
              // so you'd then ask for user input of the code and build a credential from it
              // as demonstrated in the `signInWithPhoneNumber` example above
              this.setState({
                verificationId: phoneAuthSnapshot.verificationId,
                //message: 'Not verified. Code Sent.',
              });
              RX.Alert.show(
                I18NService.translate('phone-code-sent'),
                I18NService.translate('phone-code-sent-message')
              );
              break;
            case firebase.auth.PhoneAuthState.ERROR: // or 'error'
              console.log('verification error, phoneAuthSnapshot: ', phoneAuthSnapshot);
              RX.Alert.show(
                I18NService.translate('phone-verify-error'),
                // FIXME unhandled promise rejection here?
                I18NService.translate(phoneAuthSnapshot.error!.code, {
                  default: phoneAuthSnapshot.error!.message,
                })
              );
              Analytics.analyticsEvent('errorPhoneVerify', {
                phoneNumber: phoneNumber,
                emailAddress: firebase.auth().currentUser ? firebase.auth().currentUser!.email : '',
              });
              break;

            // ---------------------
            // ANDROID ONLY EVENTS
            // ---------------------
            case firebase.auth.PhoneAuthState.AUTO_VERIFY_TIMEOUT: // or 'timeout'
              console.log('auto verify on android timed out');
              Analytics.analyticsEvent('verifyPhoneAutoTimeout', { phoneNumber: phoneNumber });
              // proceed with your manual code input flow, same as you would do in
              // CODE_SENT if you were on IOS

              this.setState({
                verificationId: phoneAuthSnapshot.verificationId,
              });
              break;
            case firebase.auth.PhoneAuthState.AUTO_VERIFIED: // or 'verified'
              // auto verified means the code has also been automatically confirmed as correct/received
              // phoneAuthSnapshot.code will contain the auto verified sms code - no need to ask the user for input.
              console.log('auto verified on android, phoneAuthSnapshot: ', phoneAuthSnapshot);
              // Example usage if handling here and not in optionalCompleteCb:
              const { verificationId, code } = phoneAuthSnapshot;
              Analytics.analyticsEvent('successPhoneVerify', { verifyType: 'autoVerify' });
              this.linkUser(verificationId, code + ''); // coercion fixes type error
              break;
          }
        },
        error => {
          // optionalErrorCb would be same logic as the ERROR case above,  if you've already handed
          // the ERROR case in the above observer then there's no need to handle it here
          console.log('error visible in optional error handler: ', error);
          // verificationId is attached to error if required
        },
        phoneAuthSnapshot => {
          // optionalCompleteCb would be same logic as the AUTO_VERIFIED/CODE_SENT switch cases above
          // depending on the platform. If you've already handled those cases in the observer then
          // there's absolutely no need to handle it here.

          // Platform specific logic:
          // - if this is on IOS then phoneAuthSnapshot.code will always be null
          // - if ANDROID auto verified the sms code then phoneAuthSnapshot.code will contain the verified sms code
          //   and there'd be no need to ask for user input of the code - proceed to credential creating logic
          // - if ANDROID auto verify timed out then phoneAuthSnapshot.code would be null, just like ios, you'd
          //   continue with user input logic.
          console.log('optional complete handler sees value:', phoneAuthSnapshot);
        }
      );
    // optionally also supports .then & .catch instead of optionalErrorCb &
    // optionalCompleteCb (with the same resulting args)
    actions.setSubmitting(false);
  };

  private async linkUser(verificationId: string, code: string) {
    const sentCredential = firebase.auth.PhoneAuthProvider.credential(verificationId, code);
    console.log('created credential based on sms: ', sentCredential);

    try {
      // If the user has a phone number already, unlink it
      if (this.state.kullkiUser && this.state.kullkiUser.phoneNumber) {
        console.log('PhoneVerify::linkUser - user had phone already, unlinking');
        await firebase.auth().currentUser!.unlink(firebase.auth.PhoneAuthProvider.PROVIDER_ID);
      }
      // FIXME link the user but we have to make sure it exists first
      //https://rnfirebase.io/docs/v5.x.x/auth/reference/User#linkWithCredential
      const receivedCredential = await firebase
        .auth()
        .currentUser!.linkWithCredential(sentCredential);
      RX.Alert.show(
        I18NService.translate('phone-link-success'),
        I18NService.translate('phone-link-success-message')
      );
      console.log('receivedCredential post-link was: ', receivedCredential);
      try {
        Analytics.setAnalyticsUserProperties({
          phone: firebase.auth().currentUser!.phoneNumber,
        });
      } catch (e) {}
      this.props.navigation.goBack();
    } catch (e) {
      console.log('Problem linking with new credential: ', e);
      const { code, message } = e;
      Analytics.analyticsEvent('errorPhoneVerify', { errorCode: code });
      RX.Alert.show(
        I18NService.translate('phone-link-error'),
        I18NService.translate(code, { default: message })
      );
    }
  }

  private onEnterVerificationCode = async (
    values: PhoneVerifyCodeFormikValues,
    actions: FormikHelpers<PhoneVerifyCodeFormikValues>
  ) => {
    const { verificationCode } = values;
    console.log('in onEnterVerificationCode, values: ' + verificationCode);

    Analytics.analyticsEvent('onEnterVerificationCode', {
      phoneNumber: this.state.userPhoneNumber,
    });
    this.linkUser(this.state.verificationId, verificationCode);
    Analytics.analyticsEvent('successPhoneVerify', { verifyType: 'manualVerify' });

    try {
      Analytics.setAnalyticsUserProperties({
        phone: firebase.auth().currentUser!.phoneNumber,
      });
    } catch (e) {}

    actions.setSubmitting(false);

    // If we verify a phone number, it will create / link the account to the phone number
    // unless Google Play Services verified it automatically. We don't really want the user to
    // log in that way, so we need to unlink the phone after verify
    // a code snippet for that is here https://stackoverflow.com/a/47198337
  };

I will say that your dependencies are out of date enough I'd schedule time to move them. For instance, I'm surprised you got through the App Store review process with the version of react-native-device-info you have (I'm a maintainer there, and I know your version has UIWebView symbols they throw warnings on). Additionally Firebase Pods 5.3.0 are well behind 6.13.0 (current), and I think you need at least 6.5.0 (or 6.8.0?) before it works well on iOS13. And react-native doesn't support 64-bit builds until 0.58, but those are required for google to accept builds now. Which is to say if it's working and your in the stores, great and keep going, but I'm surprised the app with these dependencies was allowed to go in to either app store or play store

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.

Closing this issue after a prolonged period of inactivity. If this is still present in the latest release, please feel free to create a new issue with up-to-date information.

I am facing the same issue in android
ERROR_SESSION_EXPIRED: The SMS code has expired. Please re-send the verification code to try again. (Android )
did u get any solution @booboothefool ???
can somebody help ???

got the same issue on android.

I described this issue here: https://github.com/invertase/react-native-firebase/issues/2543#issuecomment-532701811

RNFB is doing some stuff underneath and code will be always expired for you

@superdhuang @San10694
Android provides "auto verification" if Google Play service can automatically detect the incoming message (and doesn't display it!). If your test phone received otp sms, you can't use confirm function, it always return 'The SMS code has expired', please use verifyPhoneNumber function instead.

Hi,

Even we're experiencing the same issue even after receiving the OTP for the actual phone number(not a test phone number) and soon after entering the OTP, seeing the same following error :

The SMS code has expired.

@IndiaBuysPixel-Test "the same issue", which platform, what versions and all that? "the same issue" is nearly impossible to troubleshoot. I suspect if you listened to the auth listener instead of verifying directly, it would be fine, but without detail it's unknown https://rnfirebase.io/auth/usage#listening-to-authentication-state

In reply to the above comment:

We're using "@react-native-firebase/auth": "^8.0.0" and not using authListener and verifying directly using the following code.

async function verifyConfirmation(inputCode, props) {
try {
props.setDialogVisible(false);
props.setIsRouting(true);
return props.confirmation
.confirm(inputCode)
.then(() => {
props.authenticate(); // Reducer that dispatches the state to authenticated
})
.catch(error => {
console.log('Error in the phone based verfification async: ' + error);
});
} catch (error) {
console.log('Error in the phone based verification: ' + error);
}
}

Though, we're entering the password as soon as we receive the OTP, we're seeing this error message. Only a few times(~1%) , this phone auth is successful to our Mobile App users

The Auth listener works.

Hi,

The following comment in this issue : https://github.com/invertase/react-native-firebase/issues/530 helped me to resolve the issue.

  Apparently, some recent versions of Android are smart enough to receive the SMS verification code and use it to authenticate the user. This authentication happens in the background while the user still receives the verification code in an SMS. When the user tries to enter the verification code, he/she gets a message that the verification code expired, because Android has already used it (in the background) and has already logged in the user! To double-check that, check the Firebase Console. You should find that this new user has been added to the list of users.To avoid receiving the verification code expiry message, we need to set up a listener for "authentication changes." As soon as Android logs in the user in the background, this listener should navigate the user away from the login screen, in which he/she was supposed to enter the verification code.

So, I resolved the issue by checking if the firebase.auth().currentUser is null or not. Only if the user is null then I'm calling the confirm() else I'm directly redirecting to Home Screen.

@mikehardy , im using
.confirm(verificationCode)
to verify the otp but getting the sms expired issue in debugging on andriod.
Not sure, whats wrong in implementation, neither got any feasible solution yet.
Can you please help

Use the auth listener. If the OTP is auto-processed by android, you are done.
Only do a confirm user if the user is not auto-processed (which you will know because the auth listener never fired, and the user hit the "submit code" button under the code entry field in your confirm code screen.

If that's not it, then maybe your SHA-1 isn't correct for whatever version of the APK you compiled, vs the firebase console.

@mikehardy i have a similar issue but it is very inconsistent and happens only on android devices. Like among 10 android devices phone auth fails for 3 devices and for other devices it works perfectly fine.

The code that i have in place:

handleSendCode = async ({phoneNumber,}) => {
           const confirmResult = await auth().signInWithPhoneNumber(phoneNumber);
           this.setState({confirmResult})
      }

handleVerifyCode = (verificationCode) => {
    const {confirmResult} = this.state;
    const {createUserWithPhoneNumber} = this.props;
    confirmResult
        .confirm(verificationCode)
        .then((user) => {
          createUserWithPhoneNumber(user);
        });
    }

Also by your statement Use the auth listener did you mean i need to check if auth().onAuthStateChanged is triggered and check if user has property like uid(if the OTP is auto-processed by android device then we have a user with uid) and not call confirm(verificationCode) in that case ?

Some thing like this:

 onAuthStateChanged = (user) => {
    if (user?.uid) this.setState({user});
  };

  componentDidMount() {
    this.unSubscribe = auth().onAuthStateChanged(this.onAuthStateChanged);
  }

  handleSendCode = async ({phoneNumber,}) => {
           const confirmResult = await auth().signInWithPhoneNumber(phoneNumber);
           this.setState({confirmResult})
      }

handleVerifyCode = (verificationCode) => {
    const {confirmResult, user} = this.state;
    const {createUserWithPhoneNumber} = this.props;
   if (!user) { **// check for confirm only when user is not available in authStateChanged**
     confirmResult
        .confirm(verificationCode)
        .then((user) => {
          createUserWithPhoneNumber(user);
        });
     } 
 }

  componentWillUnmount() {
    this.unSubscribe();
  }

@mikehardy and others, just to confirm the above code worked for me https://github.com/invertase/react-native-firebase/issues/3058#issuecomment-638148907

@vamsiabishek , for handleVerifyCode, we are verifying only if user is not present according to your code.

But what to do in case if user is present.?

@Manoj002 it returns the same existing user object once the OTP is validated, if he is already a registered user

@vamsiabishek can u please provide the complete working code here

Worked for me

"react": "16.13.1",
"react-native": "0.63.2",
"@react-native-firebase/app": "^8.4.3",
"@react-native-firebase/auth": "^9.2.3",

````
import auth from '@react-native-firebase/auth';

class FirebaseAuthService {
public verifyPhoneNumber = (phoneNumber: string): Promise => {
return new Promise((resolve, reject) => {
auth()
.verifyPhoneNumber(phoneNumber, false, false)
.then((res) => {
resolve(res.verificationId);
})
.catch((err) => reject(err));
});
};

public confirmCode = (verificationId: string, code: string): Promise => {
const credential = auth.PhoneAuthProvider.credential(verificationId, code);
return auth()
.signInWithCredential(credential)
.then(() => Promise.resolve(true))
.catch((err) => Promise.reject(err));
};
}

const firebaseAuthService = new FirebaseAuthService();
export default firebaseAuthService;
```

this issue only show in latest android i dont know why

@andikare almost always because it auto-verified and you're still attempting to manually use the code vs listening to the auth status changed events to notice the auto-verification - that's the normal reason this happens at least

Was this page helpful?
0 / 5 - 0 ratings