首先使用 Facebook 进行身份验证,然后 Google 导致 Android 的 Firebase 错误

Authentication using Facebook at first and then Google causes an error in Firebase for Android

据我了解Firebase Docs,如果用户使用凭据验证他的帐户,如果该凭据尚未与另一个凭据关联,他应该严格使用相同的凭据登录。

换句话说,如果我使用 Google 登录创建帐户,然后(注销后)尝试使用与 Google 相同的电子邮件登录 Facebook 凭据] 凭据,我应该在 logcat:

中看到这个异常

"An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address."

是的,我毫不奇怪地得到了这个例外。但是,如果我使用 Facebook 创建一个帐户,然后尝试使用 Google 凭据登录,则该帐户的提供者 (Facebook) 将转换为 Google。这次身份验证没有失败,但这不是预期的结果。我想以某种方式将每个用户与特定凭据相关联。我应该如何解决这个问题?您可以看到下面的代码:

public class SignInActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener,
        View.OnClickListener {

    private static final String TAG = "SignInActivity";
    private static final int RC_SIGN_IN = 9001;

    private GoogleApiClient mGoogleApiClient;
    private FirebaseAuth mFirebaseAuth;
    private FirebaseAuth.AuthStateListener mFirebaseAuthListener;

    private CallbackManager mCallbackManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_sign_in);

        // Facebook Login
        FacebookSdk.sdkInitialize(getApplicationContext());
        mCallbackManager = CallbackManager.Factory.create();

        LoginButton mFacebookSignInButton = (LoginButton) findViewById(R.id.facebook_login_button);
        mFacebookSignInButton.setReadPermissions("email", "public_profile");

        mFacebookSignInButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
            @Override
            public void onSuccess(LoginResult loginResult) {
                Log.d(TAG, "facebook:onSuccess:" + loginResult);
                firebaseAuthWithFacebook(loginResult.getAccessToken());
            }

            @Override
            public void onCancel() {
                Log.d(TAG, "facebook:onCancel");
            }

            @Override
            public void onError(FacebookException error) {
                Log.d(TAG, "facebook:onError", error);
            }
        });

        // Google Sign-In
        // Assign fields
        SignInButton mGoogleSignInButton = (SignInButton) findViewById(R.id.google_sign_in_button);

        // Set click listeners
        mGoogleSignInButton.setOnClickListener(this);

        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(getString(R.string.default_web_client_id))
                .requestEmail()
                .build();
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
                .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
                .build();

        // Initialize FirebaseAuth
        mFirebaseAuth = FirebaseAuth.getInstance();

        mFirebaseAuthListener = new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                FirebaseUser user = firebaseAuth.getCurrentUser();
                if (user != null) {
                    // User is signed in
                    Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
                } else {
                    // User is signed out
                    Log.d(TAG, "onAuthStateChanged:signed_out");
                }
            }
        };
    }

    @Override
    public void onStart() {
        super.onStart();
        mFirebaseAuth.addAuthStateListener(mFirebaseAuthListener);
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mFirebaseAuthListener != null) {
            mFirebaseAuth.removeAuthStateListener(mFirebaseAuthListener);
        }
    }

    private void firebaseAuthWithGoogle(GoogleSignInAccount acct) {
        Log.d(TAG, "firebaseAuthWithGooogle:" + acct.getId());
        AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
        mFirebaseAuth.signInWithCredential(credential)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());

                        // If sign in fails, display a message to the user. If sign in succeeds
                        // the auth state listener will be notified and logic to handle the
                        // signed in user can be handled in the listener.
                        if (!task.isSuccessful()) {
                            Log.w(TAG, "signInWithCredential", task.getException());
                            Toast.makeText(SignInActivity.this, "Authentication failed.",
                                    Toast.LENGTH_SHORT).show();
                        } else {
                            startActivity(new Intent(SignInActivity.this, MainActivity.class));
                            finish();
                        }
                    }
                });
    }

    private void firebaseAuthWithFacebook(AccessToken token) {
        Log.d(TAG, "handleFacebookAccessToken:" + token);

        final AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
        mFirebaseAuth.signInWithCredential(credential)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());

                        // If sign in fails, display a message to the user. If sign in succeeds
                        // the auth state listener will be notified and logic to handle the
                        // signed in user can be handled in the listener.
                        if (!task.isSuccessful()) {
                            Log.w(TAG, "signInWithCredential", task.getException());
                            Toast.makeText(SignInActivity.this, "Authentication failed.",
                                    Toast.LENGTH_SHORT).show();
                        }

                        else {
                            startActivity(new Intent(SignInActivity.this, MainActivity.class));
                            finish();
                        }
                    }
                });
    }

    /*
    private void handleFirebaseAuthResult(AuthResult authResult) {
        if (authResult != null) {
            // Welcome the user
            FirebaseUser user = authResult.getUser();
            Toast.makeText(this, "Welcome " + user.getEmail(), Toast.LENGTH_SHORT).show();

            // Go back to the main activity
            startActivity(new Intent(this, MainActivity.class));
        }
    }
    */

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.google_sign_in_button:
                signIn();
                break;
            default:
                return;
        }
    }

    private void signIn() {
        Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
        startActivityForResult(signInIntent, RC_SIGN_IN);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        mCallbackManager.onActivityResult(requestCode, resultCode, data);

        // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
        if (requestCode == RC_SIGN_IN) {
            GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
            if (result.isSuccess()) {
                // Google Sign In was successful, authenticate with Firebase
                GoogleSignInAccount account = result.getSignInAccount();
                firebaseAuthWithGoogle(account);
            } else {
                // Google Sign In failed
                Log.e(TAG, "Google Sign In failed.");
            }
        }
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        // An unresolvable error has occurred and Google APIs (including Sign-In) will not
        // be available.
        Log.d(TAG, "onConnectionFailed:" + connectionResult);
        Toast.makeText(this, "Google Play Services error.", Toast.LENGTH_SHORT).show();
    }
}

请检查线程:https://groups.google.com/forum/#!searchin/firebase-talk/liu/firebase-talk/ms_NVQem_Cw/8g7BFk1IAAAJ 它解释了为什么会发生这种情况。这是由于 Google 电子邮件被验证而 Facebook 电子邮件未被验证的一些安全问题。

To minimize the login UI clicks without compromising the account security, Firebase Authentication has a concept of 'trusted provider', where the identity provider is also the email service provider. For example, Google is the trusted provider for @gmail.com addresses, Yahoo is the trusted provider for @yahoo.com addresses, and Microsoft for @outlook.com addresses.

In the "One Account per Email address" mode, Firebase Authentication tries to link account based on email address. If a user logins from trusted provider, the user immediately signs into the account since we know the user owns the email address.

If there is an existing account with the same email address but created with non-trusted credentials (e.g. non-trusted provider or password), the previous credentials are removed for security reason. A phisher (who is not the email address owner) might create the initial account - removing the initial credential would prevent the phisher from accessing the account afterwards.

jin l IU

我终于以这种逻辑结束了:

如果用户尝试使用 Facebook 登录,但使用给定电子邮件的用户已经存在(使用 Google 提供商)并且出现此错误:

"An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address."

因此,只需要求用户使用 Google 登录(然后默默 link Facebook 到现有帐户)

我遇到了同样的问题,您只需转到 Firebase 控制台,然后在 "Authentication" 类别中删除您想要的用户。

对我有用。

转到身份验证 > 登录提供商,单击每个电子邮件多个帐户 地址并且允许使用相同的电子邮件地址创建多个帐户就是您要查找的内容。

允许使用相同的电子邮件地址创建多个帐户正是您所需要的。

只有当您在后台查看电子邮件时才可以正常工作,这是您的用户的参考。如果您使用 Firebase Id,那么将无法保留唯一用户。

在 firebase 中,非常重要在用户首次使用 Facebook 登录时验证用户电子邮件帐户,方法是发送验证电子邮件.

验证电子邮件后,如果用户使用@gmail.com 作为电子邮件地址,您可以同时使用 Facebook 和 Gmail 登录。

Facebook Login -> Click Link in Verification Email -> Gmail Login -> Facebook Login (OK)

Facebook Login -> Gmail Login -> Click Link in Verification Email -> Facebook Login (NOT OK)

如果您在用户注销并尝试使用他们的 gmail 登录之前没有验证 Facebook 电子邮件,那么当他们使用他们的 gmail 登录时,您将无法再次使用 Facebook 登录gmail.

更新 - 如果您选择始终信任 Facebook 电子邮件。

您可以设置一个 firebase 函数(触发器),当通过 Facebook 帐户首次登录时自动将 emailVerified 设置为 true。

示例代码。

const functions = require('firebase-functions');
const admin = require('firebase-admin');

exports.app = functions.auth.user().onCreate( async (user) => {

  if (user.providerData.find(d => d && d.providerId === 'facebook.com') || user.providerData === 'facebook.com') {
    
      try {
      await admin.auth().updateUser(user.uid, {
          emailVerified: true
        })
      } catch (err) {
        console.log('err when verifying email', err)
      }
  }
})

文档:Firebase Auth Trigger

我遇到了同样的问题。所以我在

中删除了google个账号和facebook账号

firebase console > authenticate > users

您曾经在互相测试之前先登录。

正如 Someone Special 所写,Facebook 用户需要电子邮件验证以及电子邮件和密码(提供商 = 密码)。

我想如果用户使用 googe 电子邮件,它会自动获得验证状态。

需要Google解决的问题:

如果用户先登录Facebook再登录Gmail,没有完成邮箱验证就写入账号。后来邮箱验证没有任何改变,账号被覆盖

如果用户通过 Facebook 登录并验证电子邮件 (gmail),那么一切都可以。可以使用两个社交媒体提供商登录