7
votes

Firebase n'envoie pas de code de vérification au téléphone la deuxième fois

Dans mon application, j'utilise Firebase pour vérifier le numéro de téléphone de l'utilisateur. Cependant, le système de vérification est incohérent et n'envoie l'OTP que la première fois. Par exemple, j'obtiens l'OTP lorsque je me connecte pour la première fois, mais si je me déconnecte et que j'essaye de me reconnecter, je n'obtiens pas l'OTP.

Il s'agit de l'activité dans laquelle l'utilisateur est invité à entrer l'OTP:

import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.firebase.FirebaseException;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.InstanceIdResult;

import java.util.concurrent.TimeUnit;

public class EnterOTPActivity extends AppCompatActivity {
    String userPhoneNumber;

    private int autoResendCount=0, resumeCount=0;
    private final String ctryCode = "+91";
    private String verificationId;
    private FirebaseAuth mAuth;
    private TextView resendOtp, enterOtpMessage, timerTV;
    private ProgressDialog progressDialog;
    private EditText otpField;
    private boolean firstSend=true,timerOn=true;
    FirebaseFirestore db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_enter_otp);
        mAuth = FirebaseAuth.getInstance();
        otpField = findViewById(R.id.editTextOtp);
        resendOtp =  findViewById(R.id.resendOTPButton);
        timerTV = findViewById(R.id.resendTimer);
        userPhoneNumber = getIntent().getStringExtra("USER_MOB").trim();
        sendVerificationCode(userPhoneNumber);
        startResendTimer(15);
        getSupportActionBar().setTitle("Verification");
        resendOtp.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!timerOn) {
                    startResendTimer(30);
                    firstSend = false;
                    sendVerificationCode(userPhoneNumber);
                }
            }
        });
        FloatingActionButton next = findViewById(R.id.fab2);
        enterOtpMessage =  findViewById(R.id.aboutotpverif);
        enterOtpMessage.setText(enterOtpMessage.getText().toString() + " " + ctryCode + userPhoneNumber);
        progressDialog = new ProgressDialog(this);
        progressDialog.setMessage("Please wait while we check your verification code");
        progressDialog.setTitle("Verification");
        progressDialog.setCancelable(false);
        next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //check if otp is correct here
                if (otpField.getText().toString().length() == 6) {
                    verifyCode(otpField.getText().toString().trim());
                    if(!progressDialog.isShowing())
                    progressDialog.show();
                } else {
                    View parentLayout = findViewById(android.R.id.content);
                    Snackbar.make(parentLayout, "A valid verification code has 6 digits", Snackbar.LENGTH_SHORT)
                            .setAction("OKAY", new View.OnClickListener() {
                                @Override
                                public void onClick(View view) {

                                }
                            })
                            .setActionTextColor(getResources().getColor(android.R.color.holo_red_light))
                            .show();
                }
            }
        });
    }

    private void verifyCode(String code) {

        try {
            PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
            signInwithCredential(credential);

        }
        catch (Exception e) {
            // code here is executed when we don't get the OTP and enter a wrong one
            progressDialog.dismiss();
            progressDialog.setCancelable(true);
            View parentLayout = findViewById(android.R.id.content);
            Snackbar.make(parentLayout, getString(R.string.incorrect_code_t1), Snackbar.LENGTH_LONG)
                    .setAction("OKAY", new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {

                        }
                    })
                    .setActionTextColor(getResources().getColor(android.R.color.holo_red_light))
                    .show();
            if(progressDialog.isShowing())
            progressDialog.dismiss();
        }
    }

    private void signInwithCredential(PhoneAuthCredential credential) {
        mAuth.signInWithCredential(credential).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    isRegisteredUser();
                }
                else {
                    if(progressDialog.isShowing())
                    progressDialog.dismiss();
                    View parentLayout = findViewById(android.R.id.content);
                    Snackbar.make(parentLayout, "Incorrect verification code", Snackbar.LENGTH_SHORT)
                            .setAction("OKAY", new View.OnClickListener() {
                                @Override
                                public void onClick(View view) {

                                }
                            })
                            .setActionTextColor(getResources().getColor(android.R.color.holo_red_light))
                            .show();
                    if(progressDialog.isShowing())
                    progressDialog.dismiss();

                }
            }
        });

    }

    private void sendVerificationCode(String number) {
        final String phoneNumber = ctryCode + number;
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                phoneNumber,        // Phone number to verify
                120,                 // Timeout duration
                TimeUnit.SECONDS,   // Unit of timeout
                this,               // Activity (for callback binding)
                mCallbacks);        // OnVerificationStateChangedCallbacks
    }

    private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

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

        @Override
        public void onVerificationCompleted(@NonNull PhoneAuthCredential phoneAuthCredential) {
            String code = phoneAuthCredential.getSmsCode();
            if (code != null) {
                verifyCode(code);
                otpField.setText(code);
                if(!progressDialog.isShowing())
                progressDialog.show();
            }
        }

        @Override
        public void onVerificationFailed(@NonNull FirebaseException e) {
            Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        }
    };

    private void isRegisteredUser() {

        final String userId = ctryCode + userPhoneNumber;
        db = FirebaseFirestore.getInstance();
        db.collection("users")
                .whereEqualTo("phone", userId)
                .get()
                .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {

                    @Override
                    public void onComplete(@NonNull Task<QuerySnapshot> task) {
                        try{
                        if (task.isSuccessful()) {

                            saveToken(userId);

                            for (QueryDocumentSnapshot document : task.getResult()) {
                                if (document.getId().equalsIgnoreCase(userId)) {

                                    Intent startmainact = new Intent(EnterOTPActivity.this, MainActivity.class);
                                    startmainact.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                                    startActivity(startmainact);
                                    finish();
                                    return;
                                }
                                //Log.d(TAG, document.getId() + " => " + document.getData());
                            }

                            Intent startposact = new Intent(EnterOTPActivity.this, ParOrStudActivity.class);

                            startposact.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                            startActivity(startposact);
                            finish();

                        } else {
                            View parentLayout = findViewById(android.R.id.content);
                            Snackbar.make(parentLayout, "Database error: please try again", Snackbar.LENGTH_SHORT)
                                    .setAction("OKAY", new View.OnClickListener() {
                                        @Override
                                        public void onClick(View view) {

                                        }
                                    })
                                    .setActionTextColor(getResources().getColor(android.R.color.holo_red_light))
                                    .show();
                        }
                    }
                        catch(Exception e){
                            View parentLayout = findViewById(android.R.id.content);
                            Snackbar.make(parentLayout, "Database error: please try again", Snackbar.LENGTH_SHORT)
                                    .setAction("OKAY", new View.OnClickListener() {
                                        @Override
                                        public void onClick(View view) {

                                        }
                                    })
                                    .setActionTextColor(getResources().getColor(android.R.color.holo_red_light))
                                    .show();
                        }
                    }

                });
    }

    private void saveToken(final String userId) {
        FirebaseInstanceId.getInstance().getInstanceId()
                .addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
                    @Override
                    public void onComplete(@NonNull Task<InstanceIdResult> task) {
                        if (!task.isSuccessful()) {
                            return;
                        }

                        // Get new Instance ID token
                        String token = task.getResult().getToken();
                        FirebaseFirestore.getInstance().collection("users").document(userId).update("token",token);
                    }
                });
    }


    @Override
    protected void onResume() {
        super.onResume();
        if (autoResendCount<2 && resumeCount>0) {
            sendVerificationCode(userPhoneNumber);
            autoResendCount=autoResendCount+1;
            resumeCount=resumeCount+1;
        }
    }


    @Override
    protected void onStop() {
        super.onStop();
        progressDialog.dismiss();
    }

    public void startResendTimer(int seconds) {
        timerTV.setVisibility(View.VISIBLE);
        resendOtp.setEnabled(false);

        new CountDownTimer(seconds*1000, 1000) {

            public void onTick(long millisUntilFinished) {
                String secondsString = Long.toString(millisUntilFinished/1000);
                if (millisUntilFinished<10000) {
                    secondsString = "0"+secondsString;
                }
                timerTV.setText(" (0:"+ secondsString+")");
            }

            public void onFinish() {
                resendOtp.setEnabled(true);
                timerTV.setVisibility(View.GONE);
                timerOn=false;
            }
        }.start();
    }

    @Override
    public void onBackPressed() {
        // do not allow user to go back to the screen before
    }
}

Ce système de vérification erratique délivre une UX incompétente. Y a-t-il un moyen de le contourner?


3 commentaires

Après vous être déconnecté et accéder à EnterOTPActivity pour la deuxième fois, essayez de vérifier cette ligne: userPhoneNumber = getIntent().getStringExtra("USER_MOB").trim() . Avez-vous toujours le bon userPhoneNumber?


Oui. Après s'être déconnecté, l'utilisateur est ramené à l'écran de connexion où il saisit à nouveau son numéro de téléphone.


Veuillez vérifier ma réponse. Je pense que c'est la cause première de votre problème.


5 Réponses :


1
votes

Vous essayez simplement de vous déconnecter de votre application ou une autre chose que vous pouvez faire est d'effacer les données de l'application ou vous pouvez réinstaller.


1 commentaires

Oui, mais nous ne pouvons pas nous attendre à ce que chaque utilisateur fasse cela.



8
votes

Si vous lisez la documentation Firebase sur PhoneAuthProvider , il est clairement indiqué:

La méthode verifyPhoneNumber est réentrante: si vous l'appelez plusieurs fois, comme dans la méthode onStart d'une activité, la méthode verifyPhoneNumber n'enverra pas de deuxième SMS à moins que la demande d'origine n'ait expiré.

Dans votre cas, vous définissez le délai d'expiration à 120 secondes . Si vous voulez vraiment tester ce comportement, je suggère de réduire le délai à 10 ou 20 secondes, et de voir si vous pouvez renvoyer OTP après le délai.

De plus, si vous souhaitez obtenir l'événement timeout, vous pouvez ajouter onCodeAutoRetrievalTimeOut dans le cadre de PhoneAuthProvider.OnVerificationStateChangedCallbacks . Je pense qu'il est sûr de définir votre resendOtp activé comme vrai dans ce rappel. Et c'est bien mieux que de définir votre propre minuterie.


6 commentaires

J'ai essayé d'obtenir le code en essayant à nouveau après quelques heures. Toujours pas de succès.


Avez-vous essayé ceci: PhoneAuthProvider.getInstance().verifyPhoneNumber(phoneNumbe‌​r, 10, TimeUnit.SECONDS, this, mCallbacks); puis attendez 10 secondes avant de réessayer? Parce que je suis à peu près sûr que le problème réside ici.


Ouais, ça a du sens. Je l'ai essayé et cela a fonctionné. Il y a un numéro de téléphone pour lequel il n'a pas fonctionné, et celui-ci a été connecté et déconnecté plusieurs fois au cours des deux dernières semaines. Un moyen de contourner cela?


Le numéro de téléphone existe-t-il dans le même appareil? Si vous testez sur différents appareils, assurez-vous qu'ils ont tous la même version APK (celle avec les dernières modifications).


Ouais, le numéro de téléphone est sur le même appareil


Je viens de constater que ce n'est pas le seul numéro confronté à ce problème.



3
votes
@Override
public void onVerificationCompleted(PhoneAuthCredential credential) {
    // This callback will be invoked in two situations:
    // 1 - Instant verification. In some cases the phone number can be instantly
    //     verified without needing to send or enter a verification code.
    // 2 - Auto-retrieval. On some devices Google Play services can automatically
    //     detect the incoming verification SMS and perform verification without
    //     user action.
    Log.d(TAG, "onVerificationCompleted:" + credential);

    String code = phoneAuthCredential.getSmsCode();

        Log.d("REGISTER_USER", "code generated first " + code);
        if (code != null) {
            //verifying the code like in normal flow
            verifyUserAuthCode(code);
        } else {
           //you dont get any code, it is instant verification
            showProgressBar(WAIT_TITLE, VERIFYING_MESSAGE);
            signInWithPhoneAuthCredential(phoneAuthCredential);
        }
}
    private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
          auth.signInWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    final FirebaseUser user = auth.getCurrentUser();

                    if (task.isSuccessful()) {//user verified}

            });
}
Read above comments in official Google docs carefully, firebase uses Instant verification without sending phone number, for this you just need to validate "credentials" object , write logs you will find that this method will be executed everytime you send the verification code request

0 commentaires

-1
votes

Si votre numéro est vérifié par les services Google Play, Google vous connectera automatiquement sans envoyer de SMS.


1 commentaires

Ce n'est pas vrai



0
votes

Vous devez déconnecter votre utilisateur Firebase de votre application.

Aussi si vous donnez plusieurs demandes à partir d'un seul numéro. Firebase le traite comme un pirate informatique et le bloque.

Ajoutez celui de votre numéro comme numéro de téléphone du testeur. Vous devez également définir votre code de vérification. Ensuite, vous pouvez utiliser ce code de vérification sur l'écran de vérification pour vous connecter.


0 commentaires