Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to merge emailAndPasswordAuth with PhoneAuth in Firebase?

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.

Two Accounts on one Phone

like image 560
meditat Avatar asked Jun 17 '18 10:06

meditat


1 Answers

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:-

  1. get AuthCredential for the new authentication provider
  2. 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();
                    }
                }
            });
}
like image 99
Kruti Parekh Avatar answered Sep 28 '22 03:09

Kruti Parekh