onesector
onesector

Reputation: 444

How to link Phone with Email and Password Authentication in Firebase?

I am trying to create an application where a user can do registration with a username and password, and then enters a phone number, receives an OTP code, and completes the registration form. But there is a problem. If you do this, Firebase creates two different users. How can I combine (join) them so that it appears as a single account?

Activity with entering a phone number:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_signup2)

    init()

    var phoneNumber: String

    mBackBtn.setOnClickListener {

    }

    mNextBtn.setOnClickListener {
        phoneNumber = "+" + mCountyCode!!.selectedCountryCode + mPhoneNumberEd.text.toString()
        Toast.makeText(this, phoneNumber, Toast.LENGTH_LONG).show()

        sendVerificationCode(phoneNumber)
    }
}

private fun sendVerificationCode(phone: String) {
    var phoneShadow = phone
    Log.d("MyTag", phoneShadow)

    val options = PhoneAuthOptions.newBuilder()
        .setPhoneNumber(phoneShadow)
        .setTimeout(60L, TimeUnit.SECONDS)
        .setActivity(this)
        .setCallbacks(callbacks)
        .build()
    PhoneAuthProvider.verifyPhoneNumber(options)
}

private fun init() {
    callbacks = object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

        override fun onVerificationCompleted(credential: PhoneAuthCredential) {
            Log.d(TAG, "onVerificationCompleted:$credential")

            val intent = Intent(applicationContext, Signup3::class.java)
            startActivity(intent)
        }

        override fun onVerificationFailed(e: FirebaseException) {
            Log.d(TAG, "onVerificationFailed", e)
            Toast.makeText(applicationContext, "Failed", Toast.LENGTH_LONG).show()
        }

        override fun onCodeSent(verificationId: String, token: PhoneAuthProvider.ForceResendingToken) {
            Log.d("MyTag","onCodeSent:$verificationId")

            var storedVerificationId: String = verificationId
            var resendToken: PhoneAuthProvider.ForceResendingToken = token

            val intent = Intent(applicationContext, Signup3::class.java)
            intent.putExtra("storedVerificationId", storedVerificationId)
            startActivity(intent)
        }
    }

    mNextBtn = findViewById(R.id.signup2_next_btn)
    mBackBtn = findViewById(R.id.signup2_back_btn)
    mPhoneNumberEd = findViewById(R.id.signup2_phone_number_ed)
    mCountyCode = findViewById(R.id.signup2_county_code)
    mAuth = FirebaseAuth.getInstance()
}

Activity with entering the code that the phone receives:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_signup3)

    init()

    var storedVerificationId = intent.getStringExtra("storedVerificationId").toString()

    mConfirmBtn.setOnClickListener {
        var code = mPinView.text.toString().trim()

        if (code.isEmpty()) {
            Toast.makeText(this, "Ты ввёл?", Toast.LENGTH_LONG).show()
        } else {
            var credential : PhoneAuthCredential = PhoneAuthProvider.getCredential(storedVerificationId!!, code)
            signInWithPhoneAuthCredential(credential)
        }
    }
}

private fun init() {
    mConfirmBtn = findViewById(R.id.signup3_confirm_btn)
    mPinView = findViewById(R.id.signup3_pin_view)
    mAuth = FirebaseAuth.getInstance()
}

private fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential) {
    mAuth.signInWithCredential(credential)
        .addOnCompleteListener(this) {
            if (it.isSuccessful) {
                var intent = Intent(this, PhoneConfirmedSignup::class.java)
                startActivity(intent)
                finish()
            } else {
                if (it.exception is FirebaseAuthInvalidCredentialsException) {
                    Toast.makeText(this, "Invalid OTP", Toast.LENGTH_SHORT).show()
                }
            }
        }
}

Upvotes: 2

Views: 1300

Answers (2)

oriohac
oriohac

Reputation: 337

What worked for me was updatePhoneNumber(), instead of using,

linkWithCredential()

val auth = FirebaseAuth.getInstance() auth.currentUser!!.linkWithCredential(credential).addOnCompleteListener(this){ } you can use

updatePhoneNumber()

val auth = FirebaseAuth.getInstance() auth.currentUser?.updatePhoneNumber(credential).addOnCompleteListener(this){ } As it is phone number that you want to merge with Email and password.

Upvotes: 0

Alex Mamo
Alex Mamo

Reputation: 138824

If you do this, the Firebase creates two different users.

That's the expected behavior since you are using two separate types of authentication.

How can I combine them so that it appears as a single account?

If you want to have only one, then you should link both of them together into a single account. According to the official documentation regarding account linking, first, you need to get the existing credentials:

val authCredential = EmailAuthProvider.getCredential(email, password)

For Java users:

AuthCredential authCredential = EmailAuthProvider.getCredential(email, password);

And then simply use the FirebaseUser#linkWithCredential(AuthCredential credential) method like in the following lines of code:

val auth = FirebaseAuth.getInstance()
auth.currentUser!!.linkWithCredential(credential).addOnCompleteListener(this) { task ->
    if (task.isSuccessful) {
        Log.d(TAG, "linkWithCredential:success")
        val user = task.result!!.user
        updateUI(user)
    } else {
        Log.w(TAG, "linkWithCredential:failure", task.exception)
        Toast.makeText(this, "Authentication failed.", Toast.LENGTH_SHORT).show()
        updateUI(null)
    }
}

And for Java users:

FirebaseAuth auth = FirebaseAuth.getInstance();
auth.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();
            updateUI(user);
        } else {
            Log.w(TAG, "linkWithCredential:failure", task.getException());
            Toast.makeText(AnonymousAuthActivity.this, "Authentication failed.", Toast.LENGTH_SHORT).show();
            updateUI(null);
        }
    }
});

Upvotes: 3

Related Questions