I am building an app that requires users to fill in the details for the sign-up screen.. once they fill in all the details and verification of details then I allow them to enter their email ID. once the email id is validated (1. blank email 2. invalid email id 3. existing email id check through Firebase) then the app will CREATE email for the user (using email password).. CREATION OF EMAIL has 3 phases.
A. Email creation itself and
B. Sending verification email to the user.
C. Waiting until the user verifies the email.
upon verification of email, I am allowing the user to enter the mobile number. On filling the mobile validation of it will happen (1. blank mobile 2. invalid mobile No 3. existing mobile check through Firebase). if validation turns positive I am asking them to verify the mobile through OTP (new activity). after OTP verification, Finally, I am storing all the user's data into firebase (except the password).
My actual problem lies here. I want users to log in through the phone OTP process or email password process. currently, I am unable to link the email and phone of the user as firebase is considering as 2 accounts. on google, I came to know that I had to link using the LinkWithCredential option. however, it's not working...
Here is my VerifyOTP codes where phone authentication happening...
public class VerifyOTP extends AppCompatActivity {
#BindView(R.id.PinViewUser)
PinView PinViewUser;
#BindView(R.id.btnVerify)
Button btnVerify;
#BindView(R.id.btnGetOTP)
Button btnGetOTP;
#BindView(R.id.btnResend)
Button btnResend;
#BindView(R.id.txtCodeVerificationText)
TextView txtCodeVerificationText;
#BindView(R.id.Verificationlabel)
TextView Verificationlabel;
#BindView(R.id.txtNoteToUser)
TextView txtNoteToUser;
FirebaseAuth mAuth;
AuthCredential emailCredential;
String UserPhoneNo, CodeBySystem, PhNoThroughSignUp, PhNoThroughForgotPassword, ForwardEmailToNextClass, GetCredEmail, GetCredPass;
PhoneAuthProvider.ForceResendingToken mResendToken;
private static final String TAG = "AnonymousAuth";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RunAppOnFullScreenMode();
setContentView(R.layout.activity_verify_o_t_p);
ButterKnife.bind(this);
//get Ph No & Other Details from SignupLayout
PhNoThroughSignUp = getIntent().getStringExtra(ReUsuableCode.CustomMobileNo);
GetCredEmail = getIntent().getStringExtra("CredEmail");
GetCredPass = getIntent().getStringExtra("CredPass");
emailCredential = EmailAuthProvider.getCredential(GetCredEmail, GetCredPass);
//get Ph No from ForgotPassword
PhNoThroughForgotPassword = getIntent().getStringExtra("FPMobile");
if (PhNoThroughSignUp == null) {
UserPhoneNo = PhNoThroughForgotPassword;
} else {
UserPhoneNo = PhNoThroughSignUp;
}
Toast.makeText(this, "Email: " + GetCredEmail + "Password: " + GetCredPass + "Phone: " + UserPhoneNo, Toast.LENGTH_LONG).show();// this is working
txtCodeVerificationText.setText("Click On Get OTP Button to Get the OTP on the Mentioned Phone No\n\n Note: Standard SMS charges May Apply");
Log.d(TAG, "DetailsCollected");
// Initialize Firebase Auth
mAuth = FirebaseAuth.getInstance();
//Send OTPCode to User
btnGetOTP.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SendVerificationCodeToUser(UserPhoneNo);
Log.d(TAG, "Verification Code Sent");
txtNoteToUser.setVisibility(View.VISIBLE);
txtCodeVerificationText.setText("Enter the One Time Password received on\n " + UserPhoneNo);
Toast.makeText(VerifyOTP.this, "OTP Initiated!! Wait For Auto Verification of OTP", Toast.LENGTH_SHORT).show();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
btnVerify.setVisibility(View.VISIBLE);
}
}, 10000); // where 1000 is equal to 1 sec (1 * 1000)
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
btnResend.setVisibility(View.VISIBLE);
}
}, 60000); // where 1000 is equal to 1 sec (1 * 1000)
}
});
//ReSend OTPCode to User
btnResend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
resendVerificationCode(UserPhoneNo, mResendToken);
txtCodeVerificationText.setText("Resent verification code to " + UserPhoneNo + "\n\n Note: Standard SMS charges May Apply");
btnResend.setVisibility(View.INVISIBLE);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
btnResend.setVisibility(View.VISIBLE);
}
}, 60000); // where 1000 is equal to 1 sec (1 * 1000)
}
});
//Manual Click on VerifyButton
btnVerify.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String code = PinViewUser.getText().toString();
if (ReUsuableCode.fieldisEmpty(code)) {
PinViewUser.setError("Field is Empty");
} else {
VerifyCode(CodeBySystem, code);
}
}
});
}
//======================================= Other Private Codes==================
private void SendVerificationCodeToUser(String phNo) {
PhoneAuthOptions options =
PhoneAuthOptions.newBuilder(mAuth)
.setPhoneNumber(phNo) // Phone number to verify
.setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
.setActivity(this) // Activity (for callback binding)
.setCallbacks(mCallbacks) // OnVerificationStateChangedCallbacks
.build();
PhoneAuthProvider.verifyPhoneNumber(options);
}
public void linkCredential(AuthCredential credential) {
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "linkWithCredential:success");
FirebaseUser user = task.getResult().getUser();
Toast.makeText(VerifyOTP.this, "linkWithCredential:success", Toast.LENGTH_SHORT).show();
if (PhNoThroughSignUp == null) {
Intent GoToAnotherLayout1 = new Intent(VerifyOTP.this, SetPassword.class);
ForwardEmailToNextClass = getIntent().getStringExtra("FEmail");
GoToAnotherLayout1.putExtra("FEmail", ForwardEmailToNextClass);
startActivity(GoToAnotherLayout1);
finish();
} else {
Intent GoToAnotherLayout1 = new Intent(VerifyOTP.this, Retailer_SignUP.class);
GoToAnotherLayout1.putExtra(ReUsuableCode.MobileRegistration, "Mobile Validation Successful");
setResult(RESULT_OK, GoToAnotherLayout1);
finish();
}
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(VerifyOTP.this, "linkWithCredential:failure" + task.getException().toString(), Toast.LENGTH_SHORT).show();
}
}
});
}
private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks =
new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onCodeSent(#NonNull String s, #NonNull PhoneAuthProvider.ForceResendingToken forceResendingToken) {
super.onCodeSent(s, forceResendingToken);
// Save verification ID and resending token so we can use them later
CodeBySystem = s;
mResendToken = forceResendingToken;
Log.d(TAG, "OnCode Completed");
}
#Override
public void onVerificationCompleted(#NonNull PhoneAuthCredential phoneAuthCredential) {
Log.d(TAG, "On Verification start");
Toast.makeText(VerifyOTP.this, "OTP Verification Successful", Toast.LENGTH_SHORT).show();
String code = phoneAuthCredential.getSmsCode();
if (code != null) {
PinViewUser.setText(code);
VerifyCode(CodeBySystem, code);
Log.d(TAG, "Pre Link State");
linkCredential(emailCredential);
Log.d(TAG, "Post Link State");
}
}
#Override
public void onVerificationFailed(#NonNull FirebaseException e) {
Log.d(TAG, "Verification Failed");
if (e instanceof FirebaseAuthInvalidCredentialsException) {
Toast.makeText(VerifyOTP.this, "Failed to Verify!! Try After Some Time...", Toast.LENGTH_SHORT).show();
return;
} else if (e instanceof FirebaseTooManyRequestsException) {
Toast.makeText(VerifyOTP.this, "Too Many Attempts!! Try After Some Time...", Toast.LENGTH_SHORT).show();
return;
} else {
Toast.makeText(VerifyOTP.this, "Some Other Error Occured...", Toast.LENGTH_SHORT).show();
return;
}
}
};
private void VerifyCode(String verificationId, String code) {
PhoneAuthCredential Credential = PhoneAuthProvider.getCredential(verificationId, code);
Log.d(TAG, "Verified Code");
}
// [START resend_verification]
private void resendVerificationCode(String phoneNumber,
PhoneAuthProvider.ForceResendingToken token) {
PhoneAuthOptions options =
PhoneAuthOptions.newBuilder(mAuth)
.setPhoneNumber(phoneNumber) // Phone number to verify
.setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
.setActivity(this) // Activity (for callback binding)
.setCallbacks(mCallbacks) // OnVerificationStateChangedCallbacks
.setForceResendingToken(token) // ForceResendingToken from callbacks
.build();
PhoneAuthProvider.verifyPhoneNumber(options);
}
here are my error logs:
Please do guide me where I am missing. How to do I make a single account for the user?
I was able to get an answer to my code. Here is the website which I referred to fix the code: Learning Path for Linking Phone and Email
Steps to Link with 2nd Auth Provider of Firebase:
As per my scenario, I am first making the user enter an email ID and hence I make the user to first SIGN IN using email ID and password, which is nothing but,
Creation of Account with Email and Password
Sending Verification email (Optional step)
Waiting until User validates email ID (Optional Step)
Signing in the User using first Auth Provider (EmailAuthProvider).
After the signing process is complete. I made the user enter a mobile number so that he can proceed to validate the Ph No. In this follow the below process
Send a verification code to the user's phone
In the callbacks method of onVerificationCompleted. instead of regular signing-in with phAuth provider you have to use Linkwithcredentials(phcredentials)
which is:
a)
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
b)
linkCredential(PhCredential);
If you are using the manual verification of OTP then link the 2a and 2b step to verifybtn as well.
public void linkCredential(AuthCredential credential) {
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "linkWithCredential:success");
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(VerifyOTP.this, "linkWithCredential:failure" + task.getException().toString(), Toast.LENGTH_SHORT).show();
}
}
});
Related
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)
#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?
I am new to Android development. I am not getting OTP message from Fire base but if I enter the code manually then it works. I am not sure why I am not getting text message. Your help is highly appreciated. I am not sure whether I am doing correctly sendVerificationcode method correctly or not.
Steps Completed:
1) I added GSON file to app directory
2) I added test phone number in the firebase console
3) I added SHA1 code to fire base
4) I added SMS permission in the Android manifest file.
5) I enabled firebase authentication in Android studio
6) I tried different phone numbers too
VerifyPhoneActivity.java
public class VerifyPhoneActivity extends AppCompatActivity {
private String verificationId;
private FirebaseAuth mAuth;
private ProgressBar progressBar;
private EditText editText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_verify_phone);
mAuth = FirebaseAuth.getInstance();
progressBar = findViewById(R.id.progressbar);
editText = findViewById(R.id.editTextCode);
String phonenumber = getIntent().getStringExtra("phonenumber");
sendVerificationCode(phonenumber);
findViewById(R.id.buttonSignIn).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String code = editText.getText().toString().trim();
if (code.isEmpty() || code.length() < 6) {
editText.setError("Enter code...");
editText.requestFocus();
return;
}
verifyCode(code);
}
});
}
private void verifyCode(String code) {
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
signInWithCredential(credential);
}
private void signInWithCredential(PhoneAuthCredential credential) {
// private void signInWithCredential(PhoneAuthCredential) {
mAuth.signInWithCredential(credential)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Intent intent = new Intent(VerifyPhoneActivity.this, ProfileActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
} else {
Toast.makeText(VerifyPhoneActivity.this, task.getException().getMessage(), Toast.LENGTH_LONG).show();
}
}
});
}
private void sendVerificationCode(String number) {
progressBar.setVisibility(View.VISIBLE);
PhoneAuthProvider.getInstance().verifyPhoneNumber(
number,
60,
TimeUnit.SECONDS,
TaskExecutors.MAIN_THREAD,
mCallBack
);
}
private PhoneAuthProvider.OnVerificationStateChangedCallbacks
mCallBack = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
super.onCodeSent(s, forceResendingToken);
verificationId = s;
}
#Override
public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
String code = phoneAuthCredential.getSmsCode();
if (code != null) {
editText.setText(code);
verifyCode(code);
}
System.out.println("Hello Phone Number"+code);
}
#Override
public void onVerificationFailed(FirebaseException e) {
Toast.makeText(VerifyPhoneActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
};
}
Below code looks like not working:
#Override
public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
String code = phoneAuthCredential.getSmsCode();
// String code="000000";
if (code != null) {
editText.setText(code);
verifyCode(code);
}
System.out.println("Hello Phone Number2"+code);
}
Use like this following:
onCreate
mAuth = FirebaseAuth.getInstance();
mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks()
{
#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 verificaiton without
// user action.
Log.d(TAG, "onVerificationCompleted:" + credential);
Log.e("number","credential=-=-=>>><<>>>signInWithPhoneAuthCredential-->>");
signInWithPhoneAuthCredential(credential);
}
#Override
public void onVerificationFailed(FirebaseException e)
{
// This callback is invoked in an invalid request for verification is made,
// for instance if the the phone number format is not valid.
Log.e(TAG, "onVerificationFailed", e);
// Show a message and update the UI
// ...
}
#Override
public void onCodeSent(String verificationId,
PhoneAuthProvider.ForceResendingToken token)
{
// The SMS verification code has been sent to the provided phone number, we
// now need to ask the user to enter the code and then construct a credential
// by combining the code with a verification ID.
Log.e(TAG, "onCodeSent:" + verificationId+"<<token>>"+token);
// Save verification ID and resending token so we can use them later
mVerificationId = verificationId;
//mResendToken = token;
// ...
}
};
//String phoneNumber=Settings.PREFIX + Settings.PREFIX_PHONE;
String phoneNumber="your phone number with prefix";
Log.e("number","credential=-=-=>>>22222>>"+phoneNumber);
if(phoneNumber!=null && !phoneNumber.isEmpty())
{
startPhoneNumberVerification(phoneNumber);
}
Method:
private void startPhoneNumberVerification(String phoneNumber)
{
Log.e("startPhoneNumber","startPhoneNumberVerification------>>"+phoneNumber);
PhoneAuthProvider.getInstance().verifyPhoneNumber(
phoneNumber, // Phone number to verify
60, // Timeout duration
TimeUnit.SECONDS, // Unit of timeout
this, // Activity (for callback binding)
mCallbacks); // OnVerificationStateChangedCallbacks
Log.e("startPhoneNumber","startPhoneNumberVerification--2222222---->>"+phoneNumber);
}
Method:
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.e(TAG, "signInWithCredential:success");
} else
{
// Sign in failed, display a message and update the UI
Log.e(TAG, "signInWithCredential:failure", task.getException());
if (task.getException() instanceof FirebaseAuthInvalidCredentialsException)
{
}
}
}
});
}
I am trying to First SignIn the User using Email and Password, Then after it, I want the user's Phone Number too. So what I did I first Signed the user using his EmailAndPasswordAuthon one Activity(Custom Sign in), and after it, I Signed In the user by his Phone Number on the Next activity. But since it becomes two accounts on the same phone, and I want to merge these google and phone credentials into one account.
Then I tried to follow https://firebase.google.com/docs/auth/android/account-linking, but it is giving me the exception.
com.google.firebase.FirebaseException: User has already been linked to the given provider.
PhoneAuthActivity.java
String email = intent.getStringExtra("email");
String password = intent.getStringExtra("pass");
Toast.makeText(this, "Email" + email + "Password" + password, Toast.LENGTH_SHORT).show();// this is working
emailCredential = EmailAuthProvider.getCredential(email, password);
mPhoneNumberField = findViewById(R.id.field_phone_number);
mVerificationField = findViewById(R.id.field_verification_code);
mStartButton = findViewById(R.id.button_start_verification);
mVerifyButton = findViewById(R.id.button_verify_phone);
mResendButton = findViewById(R.id.button_resend);
mStartButton.setOnClickListener(this);
mVerifyButton.setOnClickListener(this);
mResendButton.setOnClickListener(this);
mAuth = FirebaseAuth.getInstance();
mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onVerificationCompleted(PhoneAuthCredential credential) {
Log.d(TAG, "onVerificationCompleted:" + credential);
signInWithPhoneAuthCredential(credential);
}
#Override
public void onVerificationFailed(FirebaseException e) {
Log.w(TAG, "onVerificationFailed", e);
if (e instanceof FirebaseAuthInvalidCredentialsException) {
mPhoneNumberField.setError("Invalid phone number.");
} else if (e instanceof FirebaseTooManyRequestsException) {
Snackbar.make(findViewById(android.R.id.content), "Quota exceeded.",
Snackbar.LENGTH_SHORT).show();
}
}
#Override
public void onCodeSent(String verificationId,
PhoneAuthProvider.ForceResendingToken token) {
Log.d(TAG, "onCodeSent:" + verificationId);
mVerificationId = verificationId;
mResendToken = token;
}
};
}
private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "signInWithCredential:success");
FirebaseUser user = task.getResult().getUser();
linkCredential(emailCredential);
startActivity(new Intent(PhoneActivity.this, MainActivity.class));
finish();
} else {
Log.w(TAG, "signInWithCredential:failure", task.getException());
if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
mVerificationField.setError("Invalid code.");
}
}
}
});
}
private void startPhoneNumberVerification(String phoneNumber) {
PhoneAuthProvider.getInstance().verifyPhoneNumber(
phoneNumber, // Phone number to verify
60, // Timeout duration
TimeUnit.SECONDS, // Unit of timeout
this, // Activity (for callback binding)
mCallbacks); // OnVerificationStateChangedCallbacks
}
private void verifyPhoneNumberWithCode(String verificationId, String code) {
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
signInWithPhoneAuthCredential(credential);
}
private void resendVerificationCode(String phoneNumber,
PhoneAuthProvider.ForceResendingToken token) {
PhoneAuthProvider.getInstance().verifyPhoneNumber(
phoneNumber, // Phone number to verify
60, // Timeout duration
TimeUnit.SECONDS, // Unit of timeout
this, // Activity (for callback binding)
mCallbacks, // OnVerificationStateChangedCallbacks
token); // ForceResendingToken from callbacks
}
private boolean validatePhoneNumber() {
String phoneNumber = mPhoneNumberField.getText().toString();
if (TextUtils.isEmpty(phoneNumber)) {
mPhoneNumberField.setError("Invalid phone number.");
return false;
}
return true;
}
#Override
public void onStart() {
super.onStart();
FirebaseUser currentUser = mAuth.getCurrentUser();
if (currentUser != null) {
}
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button_start_verification:
if (!validatePhoneNumber()) {
return;
}
startPhoneNumberVerification(mPhoneNumberField.getText().toString());
break;
case R.id.button_verify_phone:
String code = mVerificationField.getText().toString();
if (TextUtils.isEmpty(code)) {
mVerificationField.setError("Cannot be empty.");
return;
}
verifyPhoneNumberWithCode(mVerificationId, code);
break;
case R.id.button_resend:
resendVerificationCode(mPhoneNumberField.getText().toString(), mResendToken);
break;
}
}
public void linkCredential(AuthCredential credential) {
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "linkWithCredential:success");
FirebaseUser user = task.getResult().getUser();
Toast.makeText(PhoneActivity.this, "Merged", Toast.LENGTH_SHORT).show();
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(PhoneActivity.this, "Failed to merge" + task.getException().toString(), Toast.LENGTH_SHORT).show();
}
}
});
}
As Commented down I've used my own custom phoneAuth, not using the one provied by FirebaseUI, Please Help me merging these two accounts into one.
And In firebase Console it is forming two accounts.
I guess you are doing it the wrong way.
The flow as mentioned in documentation:
Complete the sign-in flow for the new authentication provider up to, but not including, calling one of the FirebaseAuth.signInWith methods. For example, get the user's Google ID token, Facebook access token, or email and password.
As quoted from the documentation of linking auth provider steps, it is mentioned that you should not call any FirebaseAuth.signInWith methods, rather you need to:-
get AuthCredential for the new authentication provider
Pass the AuthCredential object to the signed-in user's linkWithCredential method, like this:
mAuth.getCurrentUser().linkWithCredential(credential)
As user is already signed with one auth provider, we don't need to sign him again. We just need to link both the providers, so that he'll be able to sign in again with either of the providers.
As the flow from your code suggests, after verifying the phone number, you are signing the user in again with PhoneAuthCredential, and then you are trying to link the emailCredential; which the current signed in user is already linked to, hence the error.
This should be your code for mCallbacks:
mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onVerificationCompleted(PhoneAuthCredential credential) {
Log.d(TAG, "onVerificationCompleted:" + credential);
linkCredential(credential);
}
#Override
public void onVerificationFailed(FirebaseException e) {
Log.w(TAG, "onVerificationFailed", e);
if (e instanceof FirebaseAuthInvalidCredentialsException) {
mPhoneNumberField.setError("Invalid phone number.");
} else if (e instanceof FirebaseTooManyRequestsException) {
Snackbar.make(findViewById(android.R.id.content), "Quota exceeded.",
Snackbar.LENGTH_SHORT).show();
}
}
#Override
public void onCodeSent(String verificationId,
PhoneAuthProvider.ForceResendingToken token) {
Log.d(TAG, "onCodeSent:" + verificationId);
mVerificationId = verificationId;
mResendToken = token;
}
};
And Here is the linkCredentials method.
public void linkCredential(AuthCredential credential) {
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "linkWithCredential:success");
FirebaseUser user = task.getResult().getUser();
Toast.makeText(PhoneActivity.this, "Merged", Toast.LENGTH_SHORT).show();
moveToHome();
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(PhoneActivity.this, "Failed to merge" + task.getException().toString(), Toast.LENGTH_SHORT).show();
}
}
});
}
I agree with the answer of #kruti , Authenticating a user multiple times is wrong way.
-
First store the email and password in a variable
-
Sign In user with the phone number , this will generate a user id for that user
then you can link the Email and password with this Firebase account ( provided your data structure is fine )
get generated id through phone auth
var myid = firebase.auth().currentuser.uid
now link info with account
firebase.database().ref('Users/' + myid).push({
Email : useremail
Password : userpassword
})
I am using Firebase as the Backend for an Mobile Android Application. When a User registers, I would like to have a verification email sent to that user.
When the User Clicks the "SignUp" button, the following logic is run through: First of all a number of variables are set from the filled in boxes. Then a few checks are performed on validity of the filled in parameters. If the checks are passed, a user is created in the Database. This is all functioning..
Then I would like to send a verification email to the user to check if the email is valid. I have also put this in the onClick method of the Button but this is not functioning yet.
I do not receive the verification email.
What is the reason behind this and the fix?
My onCreate Method
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_sign_up_page);
setFontType();
screen = (EditText)findViewById(R.id.SchermNaam);
mail = (EditText)findViewById(R.id.EmailAdres);
knop = (Button)findViewById(R.id.SignUp_Button_SignUp);
firebaseAuth = FirebaseAuth.getInstance();
}
The onClick method for the "SignUp" Button:
public void onClickSignUpPage(View view){
String schermnaam = screen.getText().toString().trim();
String emailadres = mail.getText().toString().trim();
String paswoord = pass.getText().toString().trim();
if(TextUtils.isEmpty(schermnaam)){
Toast.makeText(this,"Schermnaam invullen", Toast.LENGTH_SHORT).show();
return;
}
if(TextUtils.isEmpty(emailadres)){
Toast.makeText(this,"Email invullen",Toast.LENGTH_SHORT).show();
return;
}
if(!schermnaam_ok(schermnaam)){
Toast.makeText(this,"schermnaam minstens 5 en maximum 15 tekens", Toast.LENGTH_SHORT).show();
return;
}
if(!paswoord_ok(paswoord)){
Toast.makeText(this,"paswoord tussen 6-12 karakters en minstens 1 cijfer", Toast.LENGTH_SHORT).show();
return;
}
firebaseAuth.createUserWithEmailAndPassword(emailadres.trim(),paswoord)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if(task.isSuccessful()){
Toast.makeText(SignUpPage.this, "Nieuwe Speler Geregistreerd", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(SignUpPage.this, SignInPage.class);
startActivity(intent);
}
else {
FirebaseAuthException e = (FirebaseAuthException) task.getException();
Toast.makeText(SignUpPage.this,"Fout in de SignUp"+e.getMessage(), Toast.LENGTH_SHORT).show();
Log.d("LoginActivity", "Failed Registration", e);
return;
}
}
});
FirebaseUser user = firebaseAuth.getCurrentUser();
user.sendEmailVerification()
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "Email sent.");
}
}
});
}
Changed it up to a sequential procedure, as suggested by the post quoted by #cramopy. Thank you for this!
Now you first need to "Sign Up" as a user, and then verify in another step, through another button. Then I receive the confirmation email.
Here my code for the onClick method for the 2 Buttons.. From a UX point of view, need to look at how to position the buttons. This is a functional viewpoint.
public void onClickSignUpPage(View view){
String schermnaam = screen.getText().toString().trim();
String emailadres = mail.getText().toString().trim();
String paswoord = pass.getText().toString().trim();
if(TextUtils.isEmpty(schermnaam)){
Toast.makeText(this,"Schermnaam invullen", Toast.LENGTH_SHORT).show();
return;
}
if(TextUtils.isEmpty(emailadres)){
Toast.makeText(this,"Email invullen",Toast.LENGTH_SHORT).show();
return;
}
if(!schermnaam_ok(schermnaam)){
Toast.makeText(this,"schermnaam minstens 5 en maximum 15 tekens", Toast.LENGTH_SHORT).show();
return;
}
if(!paswoord_ok(paswoord)){
Toast.makeText(this,"paswoord tussen 6-12 karakters en minstens 1 cijfer", Toast.LENGTH_SHORT).show();
return;
}
firebaseAuth.createUserWithEmailAndPassword(emailadres.trim(),paswoord)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if(task.isSuccessful()){
Toast.makeText(SignUpPage.this, "Nieuwe Speler Geregistreerd", Toast.LENGTH_SHORT).show();
}
else {
FirebaseAuthException e = (FirebaseAuthException) task.getException();
Toast.makeText(SignUpPage.this,"Fout in de SignUp"+e.getMessage(), Toast.LENGTH_SHORT).show();
Log.d("LoginActivity", "Failed Registration", e);
return;
}
}
});
AddDataFireBase();
}
public void onClickVerify(View view){
FirebaseUser user = firebaseAuth.getCurrentUser();
assert user != null;
user.sendEmailVerification()
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "Email sent.");
}
else {
Log.e(TAG, "sendEmailVerification", task.getException());
Toast.makeText(SignUpPage.this,
"Failed to send verification email.",
Toast.LENGTH_SHORT).show();
}}
});
Intent intent = new Intent(SignUpPage.this, SignInPage.class);
startActivity(intent);
}
Your code should work. But I suggest you to follow the next steps to verify the email:
Create account
Check if creation was successful
Check if the email is verified with user.isEmailVerified()
Reload your user to update the instance with user.reload(). You should do this because some methods on Firebase ask you to reload or reauthenticate the user before realize some operations. Reload in this case will update the cache and data of your user.
Send the email using sendEmailVerification()
Something like:
authInstance.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener {
if (it.isSuccessful) {
//Create accountData in the database
if (it.result!!.user.isEmailVerified) {
it.result!!.user.reload()
.addOnCompleteListener {
if (it.isSuccessful) {
authInstance.currentUser!!.sendEmailVerification()
.addOnCompleteListener {
Log.i("TAG", "Yay! verificationSent")
}
} else {
//Manage error
}
}
}
} else {
//Manage error
}
}
Also, you should know that sometimes the email verification takes some time in Debug environments(at least from my experience I have saw delays of 1-3 minutes, but never on release versions).
Finally, before call user.verify() you should need to call again user.reload() to update the data of your user in the Firebase Cache, if not, even if you have click on Verify my email on the email sent to your account, the FirebaseAuthor.currentUser().isEmailVerified() will continue returning you false.
So I have been trying to solve this problem but I can't seem to know what's wrong. I have a button that when clicked calls PhoneAuthProvider which has 4 options: OnVerifiicationCompleted, OnVerificationFailed, OnCodeSent, and onCodeAutoRetrieval. The problem is that oncodesent is being called but the code that I put in there doesn't work, not even a debug log. All I get is an SMS message showing a verification code. And when I set a string value to onCodeSent's verificationID, the string is value: null. Here's my code:
sendLink.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popSound.start();
if (greenCheck.getVisibility() == View.VISIBLE) {
//send link
// we will finish this activity and send the link to the number
// an option to resend code to the number: it will be provided at the link
// in this format: resend code to (XXX)-XxX-XXXX
String number = phoneNumber.getText().toString();
phoneNumber.setText("");
sendLink.setEnabled(false);
PhoneAuthProvider.getInstance().verifyPhoneNumber(
number,
60,
TimeUnit.SECONDS,
MobileNumberActivity.this,
new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
signInWithPhoneAuthCredential(phoneAuthCredential);
}
#Override
public void onVerificationFailed(FirebaseException e) {
}
#Override
public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
super.onCodeSent(s, forceResendingToken);
// verificationID never gets assigned s and it is null. log.d isn't on logcat
verificationID = s;
Log.d("testing", "onCodeSent: " + verificationID);
}
#Override
public void onCodeAutoRetrievalTimeOut(String s) {
super.onCodeAutoRetrievalTimeOut(s);
}
}
);
Intent i = new Intent(MobileNumberActivity.this, VerificationActivity.class);
startActivity(i);
finish();
} else if (phoneNumber.getText().toString().length() == 0) {
Toast.makeText(MobileNumberActivity.this, "Please enter a phone number", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MobileNumberActivity.this, "Please enter a valid phone number", Toast.LENGTH_SHORT).show();
}
}
});
Finally found the answer after trial and error. I don't know quite why this works but my guess is because since I am creating an instance of PhoneAuthProvider, it only works in oncreate. I had mine in oncreate but it was surrounded by a setOnClickListener as you see in the code above. So what worked for me is to start an intent to direct it to my verification activity, which in its oncreate method I created an instance of PhoneAuthProvider just by itself and it worked.
First activity:
sendLink.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popSound.start();
if (greenCheck.getVisibility() == View.VISIBLE) {
//send link
number = phoneNumber.getText().toString();
phoneNumber.setText("");
sendLink.setEnabled(false);
Intent i = new Intent(MobileNumberActivity.this, VerificationActivity.class);
startActivity(i);
finish();
} else if (phoneNumber.getText().toString().length() == 0) {
Toast.makeText(MobileNumberActivity.this, "Please enter a phone number", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MobileNumberActivity.this, "Please enter a valid phone number", Toast.LENGTH_SHORT).show();
}
}
});
Second activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_verification);
mAuth = FirebaseAuth.getInstance();
mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#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("COmpleted", "onVerificationCompleted:" + credential);
signInWithPhoneAuthCredential(credential);
}
#Override
public void onVerificationFailed(FirebaseException e) {
// This callback is invoked in an invalid request for verification is made,
// for instance if the the phone number format is not valid.
Log.w("failed", "onVerificationFailed", e);
if (e instanceof FirebaseAuthInvalidCredentialsException) {
// Invalid request
// ...
} else if (e instanceof FirebaseTooManyRequestsException) {
// The SMS quota for the project has been exceeded
// ...
}
// Show a message and update the UI
// ...
}
#Override
public void onCodeSent(String verificationId,
PhoneAuthProvider.ForceResendingToken token) {
// The SMS verification code has been sent to the provided phone number, we
// now need to ask the user to enter the code and then construct a credential
// by combining the code with a verification ID.
Log.d("codesent", "onCodeSent:" + verificationId);
// Save verification ID and resending token so we can use them later
mVerificationID = verificationId;
mResendToken = token;
// ...
}
};
PhoneAuthProvider.getInstance().verifyPhoneNumber(
MobileNumberActivity.number, // Phone number to verify
60, // Timeout duration
TimeUnit.SECONDS, // Unit of timeout
this, // Activity (for callback binding)
mCallbacks);
}