如何在 Firebase 中将 emailAndPasswordAuth 与 PhoneAuth 合并?

Posted

技术标签:

【中文标题】如何在 Firebase 中将 emailAndPasswordAuth 与 PhoneAuth 合并?【英文标题】:How to merge emailAndPasswordAuth with PhoneAuth in Firebase? 【发布时间】:2018-11-26 11:54:33 【问题描述】:

我正在尝试首先使用Email and Password 登录用户,然后在它之后,我也想要user's Phone Number。所以我所做的我首先在一个活动(自定义登录)上使用他的EmailAndPasswordAuth为用户签名,然后,我在下一个活动中通过他的电话号码登录用户。但是由于它变成了同一部手机上的两个帐户,我想将这些 google 和手机凭据合并到一个帐户中。

然后我尝试关注https://firebase.google.com/docs/auth/android/account-linking,但它给了我例外。

com.google.firebase.FirebaseException:用户已链接到给定的提供者。

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();
                    

                
            );

正如评论所说,我使用了我自己的自定义 phoneAuth,而不是使用 FirebaseUI 提供的那个,请帮助我将这两个帐户合并为一个。

在 firebase 控制台中,它正在形成两个帐户。

【问题讨论】:

您没有正确初始化电话凭证。您需要构建自己的手机身份验证流程,因为 FirebaseUI 不处理链接,然后在使用 FirebaseUI 注册的用户上进行 linkWithCredential。 嘿,我已经构建了自己的自定义 PhoneAuth,但同样的问题仍然存在。 :( 【参考方案1】:

我同意answer of @kruti ,多次验证用户是错误的方式。

-

首先将电子邮件和密码存储在一个变量中

-

用电话号码登录用户,这将为该用户生成一个用户ID

然后您可以将电子邮件和密码与此 Firebase 帐户相关联(前提是您的数据结构良好)

通过手机验证获取生成的id

 var myid = firebase.auth().currentuser.uid

现在将信息与帐户关联

firebase.database().ref('Users/' + myid).push(
Email : useremail
Password : userpassword
)

【讨论】:

最好使用指向另一个答案的链接,而不是声明“上述答案”。对我来说,另一个答案目前低于这个...... 如果我使用 google 或 facebook 登录用户怎么办,那么我也需要电话号码,但是 googleIdToken 在用户登录之前不会生成。不是吗? 冥想这在这种情况下可能会有所帮助firebase.google.com/docs/auth/android/account-linking meditat 它将保留 id 直到您在手机验证后保存用户数据【参考方案2】:

我猜你做错了。

文档中提到的流程:

完成新身份验证提供程序的登录流程,直至(但不包括)调用 FirebaseAuth.signInWith 方法之一。例如,获取用户的 Google ID 令牌、Facebook 访问令牌或电子邮件和密码。

从documentation of linking auth provider steps 中引用,提到你不应该调用任何FirebaseAuth.signInWith 方法,而是你需要:-

    获取身份验证提供程序的 AuthCredential

    AuthCredential 对象传递给登录用户的linkWithCredential 方法,如下所示:

    mAuth.getCurrentUser().linkWithCredential(credential)

由于用户已经与一个身份验证提供商签署,我们不需要再次签署他。我们只需要链接这两个提供商,这样他就可以使用其中一个提供商再次登录。

正如您的代码流程所示,在验证电话号码后,您正在使用PhoneAuthCredential 再次登录用户,然后您尝试链接emailCredential;当前登录的用户已经链接到它,因此出现错误。

这应该是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;
        
    ;

这是linkCredentials 方法。

    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();
                    
                
            );

【讨论】:

我已经提到了您需要替换的代码。 你能说出它与预期的不正确吗? 您能解释一下原因吗?还是你这么说是为了可读性? 让我们continue this discussion in chat. 在 firebase 控制台中仍然显示两个帐户 ID?

以上是关于如何在 Firebase 中将 emailAndPasswordAuth 与 PhoneAuth 合并?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Flutter 移动应用中将 Azure AD 用户登录到 Firebase?

如何在 Firebase 中将多个 Recyclerview 设置为单个适配器(多视图类型)?

如何在android中将日期转换为firebase时间戳格式

如何在颤动中将数据从复选框存储到 Firebase

如何在 SwiftUI 中将 Firebase 结果转换为 List

如何使用nodejs在对话流履行中将数据附加到firebase?