The sms code has expired. Please re-send the verification code to try again
Asked Answered
G

5

17

Whenever I tried to login with phone number using react-native-firebase sdk, I recieve OTP code through sms and when I submit the recieved code, an error is there saying:"The sms code has expired. Please re-send the verification code to try again." And here point to be noted that an entry for respective phone number is writing in Users section of firebase even there is an error.

I am using following:

NodeJS: v8.11.1,
NPM: v5.6.0,
react-native: "^0.59.9",
react-native-firebase: "^5.5.3"

Some links I have already tried are:

1. https://github.com/invertase/react-native-firebase- 
docs/blob/master/docs/auth/phone-auth.md
2. https://mcmap.net/q/744575/-firebase-phone-authentication-error-the-sms-code-has-expired 
authentication-error-the-sms-code-has-expired
3. https://www.bountysource.com/issues/45308170-firebase-phone- 
number-auth-integration
4. https://medium.com/@chrisbianca/getting-started-with-firebase- 
authentication-on-react-native-a1ed3d2d6d91
5. https://invertase.io/oss/react-native-firebase/v6/auth/phone- 
auth

In MobileRegistration.js:

navigateToOtpScreen() {
console.log("Phone number: ", "+91" + this.state.phoneNumber)
firebase.auth().signInWithPhoneNumber("+91" + 
this.state.phoneNumber)
.then(confirmResult => {
   this.setState({ confirmResult, message: 'Code has been sent!'})
   this.props.navigation.navigate('EnterOtp', { 'confirmResult': 
   confirmResult})
 })
 .catch(error => {
      alert(error.message);
      this.setState({ message: `Sign In With Phone Number Error: 
      ${error.message}` })
  });

};

In EnterOtp.js:

componentDidMount() {
this.unsubscribe = firebase.auth().onAuthStateChanged((user) => {
    alert(JSON.stringify(user));
    if (user) {
        this.setState({
            user: user.toJSON()
        });
    } else {
        // User has been signed out, reset the state
        this.setState({
            user: null,
            message: '',
            otp: '',
            otp1: '',
            otp2: '',
            otp3: '',
            otp4: '',
            otp5: '',
            otp6: ''

        });
    }
});

}

componentWillUnmount() {
if (this.unsubscribe) this.unsubscribe();

}

verifyOTP = async () => {
const {
    confirmResult,
} = this.props.navigation.state.params;
let otp = this.state.otp + "" + this.state.otp1 + "" + this.state.otp2 + "" + this.state.otp3 + "" + this.state.otp4 + "" + this.state.otp5 + "" + this.state.otp6
if (confirmResult && otp.length) {

    alert(otp);
    confirmResult.confirm(otp).then((user) => {
            AsyncStorage.setItem('accessToken', confirmResult._verificationId);
            this.props.navigation.navigate('SetupCoverVideo');
            this.setState({
                message: 'Code Confirmed!'
            });
        })
        .catch(error => {
            alert(error.message) && this.setState({
                message: `Code Confirm Error: ${error.message}`
            })
        });
}

}

Expected Result: Code should be verified and an entry should be in Users section of firebase and navigate to SetupCoverVideo.

Actual Result: Facing an error saying: "The sms code has expired. Please re-send the verification code to try again." And here point to be noted that an entry for respective phone number is writing in Users section of firebase even there is an error.

I am wondering for the solution. Anyone please assist me.

Gabel answered 16/7, 2019 at 10:10 Comment(2)
Have you got solution?Andraandrade
I havent found answer for this anywhere..Bauxite
D
27

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. The following demonstrates how this can be implemented. I would add the following code to the login screen.

Example code for use with functional components:

useEffect( () => {
    firebase.auth().onAuthStateChanged( (user) => {
        if (user) {
            // Obviously, you can add more statements here, 
            //       e.g. call an action creator if you use Redux. 

            // navigate the user away from the login screens: 
            props.navigation.navigate("PermissionsScreen");
        } 
        else 
        {
            // reset state if you need to  
            dispatch({ type: "reset_user" });
        }
    });
}, []);  

Example code for use with class components:

// I did NOT test this version, because I use functional components. 
componentDidMount() {
    firebase.auth().onAuthStateChanged( (user) => {
        if (user) {
            // Obviously, you can add more statements here, 
            //       e.g. call an action creator if you use Redux. 

            // navigate the user away from the login screens: 
            props.navigation.navigate("PermissionsScreen");
        } 
        else 
        {
            // reset state if you need to 
            this.setState({
                user: null,
                messageText: '',
                codeInput: '',
                phoneNo: '',
                confirmResult: null,
            });
        }
    });
};  
Degraw answered 9/1, 2020 at 11:4 Comment(12)
what're the benefits exactly in else statement I couldn't get it?Loden
To my understanding, if your app has a "logout" functionality, the else statement would be executed. I never tried it because my app does not have a logout option.Degraw
@BilalAbdeen do you know from which Android version this feature is supported? I need to know because in case the OS does not verify automatically, some code is needed for manual verificationBackswept
@Backswept If I were you, I would make sure my code support manual verification anyway.Degraw
Yes you are right, but I need a test device that does not do auto-verification to make sure the code works correctly, hence the questionBackswept
@Backswept for this I think send verification sms to some other number and then enter the sms received on that numbe.rPenult
Is there anyway to prevent this 'smart' background verification?Blowzed
@Blowzed did you get the solution??Spermatozoon
@BilalAbdeen how can we logout the user after successful verification? Because in my case, once the otp is verified for one user, it is automatically authenticating every preceding user, irrespective of the number usedEnsepulcher
@salvishahzad It does not sound right that "it automatically authenticates every user irrespective of the number used," I suggest that you do more troubleshooting to find what exactly is happening. If you could not figure it out, I suggest that you submit a new question regarding your problem.Degraw
@BilalAbdeen I checked it, it seems the user session is maintained once it it authenticated and next time when another user requests for an OTP, the previous user is still logged inEnsepulcher
@salvishahzad Are you talking about the same device? When a user logs in to a device, he/she would stay connected forever. This is the expected behaviour. In the mobile world, the assumption is that devices are "personal." Users expect to stay logged in until they explicitly log off.Degraw
E
1

You need to check for background authentication using:

conponentDidMount() {    
firebase.auth().onAuthStateChanged((user) => {
            if (user) {
                // alert(JSON.stringify(user))
                // Obviously, you can add more statements here, 
                //       e.g. call an action creator if you use Redux. 
                // navigate the user away from the login screens: 
            }
        });
}

Then you need to log out your user once you are done with it (I faced this issue of old user session already present while new user was coming to authenticate)

So, at the time of logging out use:

if (firebase.auth().currentUser)
            firebase.auth().currentUser.delete();

And you are all set!

Ensepulcher answered 6/9, 2021 at 5:52 Comment(0)
D
0

I found that the problem is the described above (Android trying to handle automatically in background). The Solution I found was to place a listening inside the method that verify the phoneSignin. See bellow:

  Future<void> createUserWithPhone(
  {required String phoneNumber,
  required BuildContext context,
  required Function(bool esteEumNovoUser) verifyCompleted,
  required Function(String erroMsg) failed,
  required Function(String generatedCodeId) codeSent,
  required VoidCallback codeRetrieveTimeout}) async {
FirebaseAuth auth = FirebaseAuth.instance;


//create a listener that will handle the signing
var listener = FirebaseAuth.instance.authStateChanges();
//place the listener to handle the login and the automatic login in 
 background
listener.listen((User? user) {
  if (user != null) {
    debugPrint('logged in');
    final DateTime? dataCriacao = user.metadata.creationTime;
    final DateTime dataAgora = DateTime.now();

    if (dataCriacao != null) {
      debugPrint(dataCriacao.toString());
      final Duration difference = dataAgora.difference(dataCriacao);
      final int minutesDifference = difference.inMinutes;
      //if difference is short, we know its a new user
      if (minutesDifference <= 3) {
       
         newUserCreated(); //now you proceed with your code, probably 
          //creating a new entry in Firestore
         //verifyCompleted(true) in My code I use this callback
        listener.listen((event) {}).cancel();

      } else {
        
        existingUserLoggin();//was a existing user, handle the result
        //verifyCompleted(false) in My code I use this callback
        listener.listen((event) {}).cancel();
   
      }
    } else {
      debugPrint('novo user');
      newUserCreated(); //if no datacreation available, its a new user
      //verifyCompleted(true) in My code I use this callback
      listener.listen((event) {}).cancel();

    }
  } else {
    debugPrint('not logged in, do nothing');
  }
});

//keep the phoneLogin logic of firebase to create the user in backend for 
//you
await auth.verifyPhoneNumber(
  timeout: const Duration(seconds: 60),
  phoneNumber: '+55$phoneNumber',
  verificationCompleted: (PhoneAuthCredential credential) {
    debugPrint(credential.toString());
    debugPrint('Verificação completa');

    //Disconsider this block of code bellow, you are now handling it
    //on listener above.
    // ANDROID ONLY!
    // Sign the user in (or link) with the auto-generated credential
    /*
    signInWithPhone(
        credential: credential,
        createUser: () {
          verifyCompleted(true);
        },
        signinThisUser: () {
          verifyCompleted(false);
        });*/
  },
  verificationFailed: (FirebaseAuthException e) {
    debugPrint('failed');
    debugPrint(e.message);
    debugPrint(e.code);
    debugPrint(e.toString());
    if (e.code == 'invalid-phone-number') {
      failed('Você informou um número inválido');
    } else {
      failed('Ocorreu um erro. Tente novamente em instantes');
    }
  },
  codeSent: (String verificationId, int? resendToken) {
    debugPrint('code sent');
    debugPrint(verificationId);

    codeSent(verificationId);
  },
  codeAutoRetrievalTimeout: (String verificationId) {
    debugPrint('acabou o tempo de timedout');
    MySnack().displaySnackBar(context, 'entrou em 
    codeAutoRetrievalTimeout');
    codeRetrieveTimeout();
    },
   );
 }
Desalvo answered 8/11, 2023 at 1:4 Comment(0)
D
0
This error typically error when the authenticated user doesnot have 
necessary permission to read or write in firebase datastore. To resolve 
this issue you need to adjust the firestore security rules to allow access 
to the specific operation you trying to perform. 
Go the firebase-console-> select your project-> navigate to firestore-> 
click on rules tab and then modify the rules as. 
service cloud.firestore {
match /databases/{database}/documents {
// Allow read and write access to all documents
// by users authenticated with Firebase
match /{document=**} {
  allow read, write: if request.auth != null;
}

} }

Defence answered 11/3, 2024 at 7:21 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Domenic
A
0
function onAuthStateChanged(user) {
    if (user) {
      // Some Android devices can automatically process the verification code (OTP) message, and the user would NOT need to enter the code.
      // Actually, if he/she tries to enter it, he/she will get an error message because the code was already used in the background.
      // In this function, make sure you hide the component(s) for entering the code and/or navigate away from this screen.
      // It is also recommended to display a message to the user informing him/her that he/she has successfully logged in.
    }
  }

  useEffect(() => {
    const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
    return subscriber; // unsubscribe on unmount
  }, []);
Autumn answered 10/7, 2024 at 9:9 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.