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)
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.
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.
@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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With