Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase phone authentication shows exception when activity resumes: "The sms code has expired. Please re-send the verification code to try again."

I am using firebase phone authentication for verifying phone number. But, there is an issue when I try to switch any other app or just press the home button in the middle of the process is going on, i.e in between the process has started and ended. Even if the OTP is correct and the time is not expired it always shows the FirebaseAuthInvalidCredentialsException with the following message.

The SMS code has expired. Please re-send the verification code to try again.

Previously, I found out that when the activity pause and resumes back in the middle of the process is going on, the authentication process (either verification of OTP or sending the OTP ) stops and it does not resume. So, for that, I manually started the process. Now, the process starts but it returns the above exception always.

By using the method resumeProcess() in on Resume. Now, receiveOTP() works fine. but veirifcation of OTP has still the issue. (as explined just above).

I am using a dialog for phone authentication.

The code I've written for phone authentication and for the issue is as follows.

To resume the process manually, which was stopped on pause. I am using resumeProcess() method in onResume().

In fragment's onResume()

@Override
public void onResume() {
    super.onResume();
    if (phoneAuthDialog != null && phoneAuthDialog.isShowing()) {
        phoneAuthDialog.resumeProcess();
    }
}

And, In dialog...

public void resumeProcess(){
    if(isReceivingOtpSms){
        receiveOtp(phoneNumber,null);
    }

    if(isVerifyingOtp){
        verifyOtp();
    }
}

for receiving OTP.

private void receiveOtp(String phoneNumber,PhoneAuthProvider.ForceResendingToken forceResendingToken) {
    if (connectionDetector != null && connectionDetector.isConnectingToInternet()) {

        setPhoneVerificationCallback();
        isReceivingOtpSms =true;
        showProgress();

        //for receiving otp for the first time
        if(forceResendingToken==null){
            PhoneAuthProvider.getInstance().verifyPhoneNumber(
                    phoneNumber,        // Phone number to verify
                    60,                 // Timeout duration
                    TimeUnit.SECONDS,   // Unit of timeout
                    activity,               // Activity (for callback binding)
                    mCallbacks);        // OnVerificationStateChangedCallbacks
        }

        //for resending otp
        else {
            PhoneAuthProvider.getInstance().verifyPhoneNumber(
                    phoneNumber,        // Phone number to verify
                    60,                 // Timeout duration
                    TimeUnit.SECONDS,   // Unit of timeout
                    activity,               // Activity (for callback binding)
                    mCallbacks,          // OnVerificationStateChangedCallbacks
                    forceResendingToken);
        }

    } else
        showToast(activity, Constants.MESSAGE_NO_CONNECTION);
}

The setPhoneVerificationCallback() method is used for handling verificationcallback.

private void setPhoneVerificationCallback() {
    mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
        @Override
        public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
            hideProgress();               //to hide progressbar.
            isReceivingOtpSms=false;
            //some ui process....
            verifyCredentials(phoneAuthCredential); 
        }

        @Override
        public void onCodeAutoRetrievalTimeOut(String s) {
            super.onCodeAutoRetrievalTimeOut(s);
        }

        @Override
        public void onVerificationFailed(FirebaseException e) {
            e.printStackTrace();
            hideProgress();
            isReceivingOtpSms=false;

            if (e instanceof FirebaseNetworkException) {
                showToast(activity, activity.getString(R.string.err_noconnection_message));
            } else if (e instanceof FirebaseAuthInvalidCredentialsException) {
                e.printStackTrace();
                showToast(activity, "Incorrect phone number format. Check your mobile number and country code twice.");
            } else {
                showToast(activity, e.getMessage());
            }
        }

        @Override
        public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
            super.onCodeSent(verificationId, forceResendingToken);
            hideProgress();
            isReceivingOtpSms=false;
            PhoneAuthDialogRefactored.this.verificationId = verificationId;
            PhoneAuthDialogRefactored.this.forceResendingToken = forceResendingToken;

            //some ui process ...

            showToast(activity, "code sent to your number");
        }
    };
}

The verifyOTP() method

private void verifyOtp() {
    String otp = etOtp.getText().toString().trim();
    if (otp.length() == 6) {

        if (connectionDetector != null && connectionDetector.isConnectingToInternet()) {
            if (verificationId != null) {
                Log.e("Verification ID : ", verificationId);
                PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, otp.trim());
                verifyCredentials(credential);
            } else {
                showToast(activity, "Please wait for a while! the code is not sent yet.");
            }
        } else {
            showToast(activity, activity.getString(R.string.err_noconnection_message));
        }
    } else {
        errOtp.setVisibility(View.VISIBLE);
        errOtp.setText(activity.getString(R.string.err_required));
    }
}

The verifyCredentials method verifies the OTP is correct or not.

private void verifyCredentials(PhoneAuthCredential credential) {
    isVerifyingOtp=true;
    showProgress();

    if (activity != null) {
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(activity, task -> {
                    if (task.isSuccessful()) {
                        // Sign in success, update UI with the signed-in user's information
                        hideProgress();
                        isVerifyingOtp=false;
                        //some ui process...

                    } else {
                        // Sign in failed, display a message and update the UI
                        hideProgress();
                        isVerifyingOtp=false;
                        Log.w("Phone authentication", "signInWithCredential:failure", task.getException());
                        if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
                            // The verification code entered was invalid

                            Exception exception=task.getException();
                            if(exception.getMessage().equals("The sms code has expired. Please re-send the verification code to try again.")){
                                showToast(activity,exception.getMessage());
                                errOtp.setVisibility(View.VISIBLE);
                                errOtp.setText(activity.getString(R.string.err_expired_code));
                            }
                            else {
                                errOtp.setVisibility(View.VISIBLE);
                                errOtp.setText(activity.getString(R.string.err_wrong_otp));
                            }
                        }
                    }
                });
    }
}

Please help me with the issue and feel free to ask if my question is not clear. The major issue is

When even the OTP is correct and the time is not expired. It still shows the code has expired. and it happens only in the case when we pasue and resume back to the activity. in the middle of a process. (by in the middle of a process I mean, the verification process has started but before it completes its verification process (success or failure) I press switch to another app and come back to the app)

like image 669
Riddhi Avatar asked Feb 21 '19 05:02

Riddhi


People also ask

How do I add my phone number to Firebase authentication?

In the Firebase console, open the Authentication section. In the Sign in method tab, enable the Phone provider if you haven't already. Open the Phone numbers for testing accordion menu. Provide the phone number you want to test, for example: +1 650-555-3434.

How do I change the SMS verification template in Firebase phone Auth?

Unfortunately, neither the email verification template nor the SMS verification template can be modified. You can select the language from the Firebase Console, however this is a per project setting and you can't modify the templates.


1 Answers

@Riddhi I think the problem is with the verificationId which you are sending at the time of verification. The Code seems to be good. I had the same problem previously when I am sending the verificationId.

public class OtpVerificationActivity extends AppCompatActivity implements View.OnClickListener {

    EditText mobileNumber,otpText;
    Button sendOtp,verifyOtp;
    FirebaseAuth mAuth;
    String codeSent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_otp_verification);

        mobileNumber = findViewById(R.id.mobileNumber);
        otpText = findViewById(R.id.otpText);
        sendOtp = findViewById(R.id.sendOtp);
        verifyOtp = findViewById(R.id.verifyOtp);

        sendOtp.setOnClickListener(this);
        verifyOtp.setOnClickListener(this);

        mAuth = FirebaseAuth.getInstance();

    }

    @Override
    public void onClick(View v) {

        switch (v.getId()){

            case R.id.sendOtp:

                sendVerificationCode();
                break;

            case R.id.verifyOtp:

                verifyCodeSent();
                break;
        }

    }

    private void verifyCodeSent() {

        String code = otpText.getText().toString();
        PhoneAuthCredential credential = PhoneAuthProvider.getCredential(codeSent,code);
        signInWithPhoneAuthCredential(credential);
    }

    private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {
                            // Sign in success, update UI with the signed-in user's information
                            Log.d("verifyCode", "signInWithCredential:success");

                            Toast.makeText(OtpVerificationActivity.this, "Successful", Toast.LENGTH_SHORT).show();
                            //FirebaseUser user = task.getResult().getUser();
                            // ...
                        } else {
                            // Sign in failed, display a message and update the UI
                            Log.w("verifyCode", "signInWithCredential:failure", task.getException());
                            if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
                                // The verification code entered was invalid
                                Toast.makeText(OtpVerificationActivity.this, ""+task.getException().getMessage(), Toast.LENGTH_SHORT).show();
                            }
                        }
                    }
                });
    }

    private void sendVerificationCode() {

        String phoneNumber = mobileNumber.getText().toString();

        if (phoneNumber.isEmpty()){
            mobileNumber.setError("mobile number cannot be empty");
            mobileNumber.requestFocus();
        }

        if (phoneNumber.length() < 10){
            mobileNumber.setError("Please enter a valid phone");
            mobileNumber.requestFocus();
        }

        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                "+91" + phoneNumber,        // Phone number to verify (I hardcoded it only for Indian Mobile numbers).
                60,                 // Timeout duration
                TimeUnit.SECONDS,   // Unit of timeout
                this,               // Activity (for callback binding)
                mCallbacks);
    }

    PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
        @Override
        public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {

        }

        @Override
        public void onVerificationFailed(FirebaseException e) {

        }

        @Override
        public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
            super.onCodeSent(s, forceResendingToken);

            codeSent = s;
        }
    };
}

I hope it works for you. Could you get back to me after you check this code?

like image 146
Brahma Datta Avatar answered Nov 14 '22 23:11

Brahma Datta