简体   繁体   中英

Facebook login with Android using Firebase BaaS

I'm following this Firebase tutorial to integrate Facebook to my app. Everything looks good, except by the fact that I'm having this exception:

com.google.firebase.auth.FirebaseAuthUserCollisionException: An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address.

Fair enought, considering that this is exactly what I'm trying to do. First I created a code to integrate gmail to my app, and logged in using my email account. Then I tried to log-in with Facebook, and got the error.

Searching how to work this out, I found here that

FirebaseAuthUserCollisionException thrown if there already exists an account with the email address asserted by the credential. Resolve this case by calling fetchProvidersForEmail(String) and then asking the user to sign in using one of them.

Finally the problem is that I'm not sure I'm using correctly the fetchProvidersForEmail method, because my task is not being successful (check the last block of code).

Can anyone give a support? Thank you,

*The code below is currently supporting login for gmail and Facebook. The method which I'm speaking here is the last one, handleFacebookAccessToken .

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

/**
 * References
 * https://developers.google.com/identity/sign-in/android/sign-in#configure_google_sign-in_and_the_googleapiclient_object
 */

// Here a simple Sign-In button is used to initiate authentication. In this step you will
// implement the logic to Sign-In with Google then use that Google account to authenticate with
// Firebase.

private static final String TAG = "SignInActivity";
private static final int RC_SIGN_IN = 9001;
private GoogleApiClient mGoogleApiClient;
private FirebaseAuth mAuth;

private FirebaseAuth.AuthStateListener mAuthListener;

LoginButton facebookLogin;
CallbackManager cM;

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

    FacebookSdk.sdkInitialize(this.getApplicationContext());
    setContentView(R.layout.activity_sign_in);

    cM = CallbackManager.Factory.create();
    facebookLogin = (LoginButton) findViewById(R.id.sign_in_button_facebook);
    if (facebookLogin != null) {
        facebookLogin.setReadPermissions("public_profile", "email", "user_friends");
        facebookLogin.registerCallback(cM, new FacebookCallback<LoginResult>() {
            @Override
            public void onSuccess(LoginResult loginResult) {
                Log.d(TAG, "facebook:onSuccess:" + loginResult);
                handleFacebookAccessToken(loginResult.getAccessToken());
            }

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

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

    // Configure Google Sign-In to request the user data required by the app. For example, to
    // configure Google Sign-In to request users' ID and basic profile information, create a
    // GoogleSignInOptions object with the DEFAULT_SIGN_IN parameter (ID and basic profile are
    // included in DEFAULT_SIGN_IN). To request users' email addresses as well, create the
    // GoogleSignInOptions object with the requestEmail option.
    GoogleSignInOptions mGoogleSignInOptions = new GoogleSignInOptions
            .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(getString(R.string.default_web_client_id))
            .requestEmail()
            .build();

    // Create a GoogleApiClient object with access to the Google Sign-In API and the options
    // specified. Note: To use enableAutoManage, your activity must extend FragmentActivity or
    // AppCompatActivity (a subclass of FragmentActivity), both of which are part of the Android
    // Support Library. You can use GoogleApiClient in a Fragment; however, the fragment's
    // parent activity must be a FragmentActivity. If you can't extend FragmentActivity, you
    // must manually manage the GoogleApiClient connection lifecycle.
    mGoogleApiClient = new GoogleApiClient
            .Builder(this)
            .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
            .addApi(Auth.GOOGLE_SIGN_IN_API, mGoogleSignInOptions)
            .build();

    // Configure the sign in button.
    SignInButton mSignInButton = (SignInButton) findViewById(R.id.sign_in_button);
    if (mSignInButton != null) {
        mSignInButton.setSize(SignInButton.SIZE_STANDARD);
        mSignInButton.setOnClickListener(this);
    }

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

    mAuthListener = 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 onClick(View v) {
    switch (v.getId()) {
        case R.id.sign_in_button:
            signIn();
            break;
    }
}

@Override
public void onResume() {
    super.onResume();
    mAuth.addAuthStateListener(mAuthListener);
}

@Override
public void onPause() {
    super.onPause();
    if (mAuthListener != null) {
        mAuth.removeAuthStateListener(mAuthListener);
    }
}

// Handle sign-in button taps by creating a sign-in intent with the getSignInIntent method, and
// starting the intent with startActivityForResult. Starting the intent prompts the user to
// select a Google account to sign in with. If you requested scopes beyond profile, email, and
// openid, the user is also prompted to grant access to the requested resources.
private void signIn() {
    Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
    startActivityForResult(signInIntent, RC_SIGN_IN);
}

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

// Handle the result of the Google Sign In in the activity's onActivityResult method, which
// retrieve the sign-in result with getSignInResultFromIntent.
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    cM.onActivityResult(requestCode, resultCode, data);

    // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
    if (requestCode == RC_SIGN_IN) {
        GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
        handleSignInResult(result);
    }
}

// After you retrieve the sign-in result, you can check if sign-in succeeded with the isSuccess
// method. If sign-in succeeded, you can call the getSignInAccount method to get a
// GoogleSignInAccount object that contains information about the signed-in user, such as the
// user's name. You can also get the user's email address with getEmail, the user's Google ID
// (for client-side use) with getId, and an ID token for the user with with getIdToken. If you
// need to pass the currently signed-in user to a backend server, send the ID token to your
// backend server and validate the token on the server.
public void handleSignInResult(GoogleSignInResult result) {
    if (result.isSuccess()) {
        GoogleSignInAccount account = result.getSignInAccount();

        // Google Sign In was successful, authenticate with Firebase.
        firebaseAuthWithGoogle(account);

    } else {
        // Google Sign In failed
        // TODO: 26/06/2016 Handle no internet connection
        Log.e(TAG, "Google Sign In failed.");
    }
}

// Add the required firebaseAuthWithGoogle method to authenticate with the signed in Google
// account.
private void firebaseAuthWithGoogle(GoogleSignInAccount acct) {
    Log.d(TAG, "firebaseAuthWithGooogle:" + acct.getId());

    AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
    mAuth
            .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 handleFacebookAccessToken(AccessToken token) {
    Log.d(TAG, "handleFacebookAccessToken:" + token);

    // This commented block below is what is on the tutorial, but gives the exceptio explained.
    /*
    AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
    mAuth.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();
            }

            // ...
        }
    });
    */

    mAuth.fetchProvidersForEmail("i_put_here_my_email@gmail.com").addOnCompleteListener(this, new OnCompleteListener<ProviderQueryResult>() {
        @Override
        public void onComplete(@NonNull Task<ProviderQueryResult> 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();
            }
        }
    });
}

}

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

                            //((FirebaseAuthException)task.getException()).getErrorCode();

                        }

                    }

                }).addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        if(e instanceof  FirebaseAuthUserCollisionException)
                        {
                            //Handle Auth Error Here
                            Toast.makeText(SignInActivity.this, e.toString(),
                                    Toast.LENGTH_SHORT).show();

                        }else if(e instanceof FirebaseAuthInvalidUserException)
                        {
                            //Handle Auth Error Here
                            Toast.makeText(SignInActivity.this, e.toString(),
                                    Toast.LENGTH_SHORT).show();

                        }
                    }
        });

Note: If we are able to get a Error Code for those Auth exceptions then that is better than using type check(instanceOf)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM