如何处理 FirebaseAuthUserCollisionException

Posted

技术标签:

【中文标题】如何处理 FirebaseAuthUserCollisionException【英文标题】:How to handle FirebaseAuthUserCollisionException 【发布时间】:2018-03-01 13:41:38 【问题描述】:

当我尝试在我的 Android 应用程序中使用 Facebook 登录时,我开始收到 FirebaseAuthUserCollisionException 异常。

com.google.firebase.auth.FirebaseAuthUserCollisionException:一个 已存在具有相同电子邮件地址但不同的帐户 登录凭据。使用与此关联的提供商登录 电子邮件地址。

我使用 Firebase 处理注册,使用 Facebook 提供“一键式”登录方法,使用 com.facebook.login.widget.LoginButton 视图作为触发器。

这些登录方法已经有效。我能够在 Facebook 上注册一个帐户,并使用相同的方法登录该帐户。但是现在已经开始抛出这个异常了。

这是我从 Facebook 注册帐户并继续登录的代码:

private void handleFacebookAccessToken(AccessToken token) 
    final ProgressDialog dialog = new ProgressDialog(this);
    dialog.show(getString(R.string.dialog_wait));
    firebaseAuth.signInWithCredential(FacebookAuthProvider.getCredential(token.getToken()))
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() 
                @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) 
                    if (task.isSuccessful()) 
                        dialog.close();
                        registerNewUserFromSocialLogin(firebaseAuth.getCurrentUser());
                     else 
                        if(task.getException() instanceof FirebaseAuthUserCollisionException) 

                            //TODO: handle sign-in with different credentials

                         else 
                            dialog.close();
                            LoginManager.getInstance().logOut();
                            Toast.makeText(LoginActivity.this,
                                    R.string.error_login,
                                    Toast.LENGTH_SHORT).show();
                        
                    
                
            );

还有我的Gradle 文件以及当前使用的库:

compile 'com.google.firebase:firebase-auth:10.2.1'
compile 'com.facebook.android:facebook-android-sdk:[4,5)'

所以我的问题是:我不知道如何处理FirebaseAuthUserCollisionException 异常。

*** 或 Firebase 文档中的解决方案都没有帮助我。我正在寻找一种解决方案,能够通过重复的凭据登录用户,以提供“一键式”登录方法。

【问题讨论】:

您实际上可以在 Firebase 控制台中为同一电子邮件启用多个身份验证提供程序,方法是转到 Authentication>SIGN-IN-METHOD 并启用 每个电子邮件地址一个帐户。请注意,如果启用此功能,则必须手动获取用户的个人资料信息support.google.com/firebase/answer/6400716 感谢@Newest***User 的回答,但这是我正在寻找的最后一个解决方案 【参考方案1】:

当用户之前使用不同的提供商使用同一电子邮件登录时,您将收到该错误。例如,用户使用 Google 使用电子邮件 user@gmail.com 登录。然后,用户尝试使用同一电子邮件但使用 Facebook 登录。 Firebase 身份验证后端将返回该错误(帐户存在不同的凭据)。在这种情况下,您应该使用 fetchProvidersForEmail 来查找与电子邮件 user@gmail.com 关联的现有提供商,在本例中为 google.com。你 signInWithCredential 到现有的 google 帐户以证明该帐户的所有权,然后 linkWithCredential 用户最初尝试登录的 Facebook 凭据。这会合并两个帐户,因此用户将来可以使用其中任何一个登录。

当您使用single accounts per email 时会发生这种情况。如果您想允许每封电子邮件使用不同的帐户,可以在 Firebase 控制台中切换到 multiple accounts per email

这是一个例子:

mAuth.signInWithCredential(authCredential)
    .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() 
        @Override
        public void onComplete(@NonNull Task<AuthResult> task) 
            // Account exists with different credential. Assume the developer wants to
            // continue and link new credential to existing account.
            if (!task.isSuccessful() &&
                task.getException() instanceof FirebaseAuthUserCollisionException) 
                FirebaseAuthUserCollisionException exception =
                        (FirebaseAuthUserCollisionException)task.getException();
                if (exception.getErrorCode() == 
                    ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL) 
                    // Lookup existing account’s provider ID.
                    mAuth.fetchProvidersForEmail(existingAcctEmail)
                       .addOnCompleteListener(new OnCompleteListener<ProviderQueryResult> 
                          @Override
                          public void onComplete(@NonNull Task<ProviderQueryResult> task) 
                            if (task.isSuccessful()) 
                              if (task.getResult().getProviders().contains(
                                      EmailAuthProvider.PROVIDER_ID)) 
                                // Password account already exists with the same email.
                                // Ask user to provide password associated with that account.
                                ... 
                                // Sign in with email and the provided password.
                                // If this was a Google account, call signInWithCredential instead.
                                mAuth.signInWithEmailAndPassword(existingAcctEmail, password)
                                  addOnCompleteListener(new OnCompleteListener<AuthResult> 
                                    @Override
                                    public void onComplete(@NonNull Task<AuthResult> task) 
                                      if (task.isSuccessful())  
                                        // Link initial credential to existing account.
                                        mAuth.getCurrentUser().linkWithCredential(authCredential);
                                      
                                    
                                  );
                              
                            
                          
                        );
            
        
    );

【讨论】:

你可以提供fetchProvidersForEmail signInWithCredentiallinkWithCredential一起工作的例子吗? 我加了一个例子。 在我的例子中,用户已经注册了 Facebook 提供商,那么就没有密码了。关于existingAcctEmail,在哪里可以买到? 我假设您正在使用 Facebook 原生 Android SDK 来获取 Facebook 访问令牌。它还应该为您提供一种获取与该用户关联的电子邮件的方法。那就是existingAcctEmail 这是一个很好的答案,如果您使用多个身份验证选项,请确保将所有有效选项作为一个不错的解决方案呈现给用户。【参考方案2】:

这个不需要,你可以在firebaase->authentication->登录方法->高级->更改下允许多个帐户合并(一个电子邮件地址多个帐户。

Firebase 将合并相同的电子邮件地址,但会为您提供不同的用户 UID。

请参阅下面的示例。

AuthCredential authCredential =  FacebookAuthProvider.getCredential(token.getToken());
    
    mAuth.signInWithCredential(authCredential)
            .addOnCompleteListener(this, task -> 
                if (task.isSuccessful()) 
                    // Sign in success, update UI with the signed-in user's information
                    Log.d(TAG, "signInWithCredential:success");
                    FirebaseUser user = mAuth.getCurrentUser();
                    LoginFacebookGoogleActivity.this.updateUI(user);
                 else 
                    // If sign in fails, display a message to the user.
                    Log.w(TAG, "signInWithCredential:failure", task.getException());

                    if(task.getException() instanceof FirebaseAuthUserCollisionException)
                        FirebaseAuthUserCollisionException exception = (FirebaseAuthUserCollisionException) task.getException();

                        //log this bundle into the analytics to analyze which details you want to collect
                        
                    

                    Toast.makeText(LoginFacebookGoogleActivity.this, "Authentication failed " + task.getException(), Toast.LENGTH_SHORT).show();
                    LoginFacebookGoogleActivity.this.updateUI(null);
                
            );

【讨论】:

一般来说,允许用户使用一个电子邮件地址创建多个帐户是一个糟糕的主意。大多数时候,我们应该不惜一切代价避免这种情况! 我不同意,你可以使用 firebaseUserId 来确定用户是否是唯一的。 @zorayr uid 根据定义是唯一的。 “独特与否”是什么意思?

以上是关于如何处理 FirebaseAuthUserCollisionException的主要内容,如果未能解决你的问题,请参考以下文章

如何处理 UsernameNotFoundException 春季安全

如何处理c#中的错误代码

Akka 如何处理消息版本?

开玩笑测试 - 如何处理 JsonWebToken 响应

如何处理 JSON 响应

“不知道如何处理' nvcc 致命错误