web-dev-qa-db-fra.com

L'authentification utilisant d'abord Facebook, puis Google provoque une erreur dans Firebase pour Android

Si je comprends bien des Firebase Docs , si un utilisateur authentifie son compte avec un identifiant, il doit se connecter strictement en utilisant le même identifiant si le justificatif n'est pas encore lié à un autre.

En d'autres termes, si je crée un compte en utilisant la connexion Google, puis (après la déconnexion) j'essaie de me connecter avec les informations d'identification Facebook en utilisant le même e-mail que celui utilisé pour les informations d'identification Google, je devrais voir cette exception dans logcat:

"Un compte existe déjà avec la même adresse e-mail mais des informations d'identification différentes. Connectez-vous en utilisant un fournisseur associé à cette adresse e-mail."

Et oui, je reçois cette exception sans surprise. Mais si je crée un compte en utilisant Facebook, puis que j'essaie de me connecter avec les informations d'identification Google, le fournisseur de ce compte (Facebook) est converti en Google. Cette fois, l'authentification n'échoue pas mais ce n'est pas le résultat attendu. Je veux associer chaque utilisateur avec une information d'identification spécifique d'une manière. Comment dois-je résoudre ce problème? Vous pouvez voir le code ci-dessous:

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();
    }
}
26
Dorukhan Arslan

Veuillez vérifier le fil: https://groups.google.com/forum/#!searchin/firebase-talk/liu/firebase-talk/ms_NVQem_Cw/8g7BFk1IAAAJ Il explique pourquoi cela se produit. Cela est dû à un problème de sécurité avec les e-mails Google en cours de vérification, contrairement aux e-mails Facebook.

11
bojeil

Autoriser la création de plusieurs comptes avec la même adresse e-mail est ce que vous recherchez.

Account email address setting

11
fluxa

J'ai finalement terminé avec cette logique:

Si l'utilisateur essaie de se connecter avec Facebook, mais que l'utilisateur avec l'e-mail donné existe déjà (avec le fournisseur Google) et que ces erreurs se produisent:

"Un compte existe déjà avec la même adresse e-mail mais des informations d'identification différentes. Connectez-vous en utilisant un fournisseur associé à cette adresse e-mail."

Donc, il suffit de demander à l'utilisateur de se connecter à l'aide de Google (et ensuite de lier silencieusement Facebook à un compte existant)

Facebook and Google Sign In logics using firebase

9
pupadupa

Pour minimiser les clics sur l'interface utilisateur de connexion sans compromettre la sécurité du compte, l'authentification Firebase a un concept de "fournisseur de confiance", où le fournisseur d'identité est également le fournisseur de services de messagerie. Par exemple, Google est le fournisseur de confiance pour les adresses @ gmail.com, Yahoo est le fournisseur de confiance pour les adresses @ yahoo.com et Microsoft pour les adresses @ Outlook.com.

En mode "Un compte par adresse e-mail", l'authentification Firebase essaie de lier le compte en fonction de l'adresse e-mail. Si un utilisateur se connecte à partir d'un fournisseur de confiance, l'utilisateur se connecte immédiatement au compte car nous savons que l'utilisateur possède l'adresse e-mail.

S'il existe un compte existant avec la même adresse e-mail mais créé avec des informations d'identification non approuvées (par exemple, fournisseur ou mot de passe non approuvé), les informations d'identification précédentes sont supprimées pour des raisons de sécurité. Un phishing (qui n'est pas le propriétaire de l'adresse e-mail) peut créer le compte initial - la suppression des informations d'identification initiales empêcherait le phishing d'accéder au compte par la suite.

Jin Liu

7
Alexis Gamarra

Autoriser la création de plusieurs comptes avec la même adresse e-mail est ce que vous recherchez.

Cela fonctionne très bien UNIQUEMENT si vous vérifiez l'e-mail dans votre backend et c'est la référence pour vos utilisateurs. Si vous utilisez l'identifiant Firebase, cela ne permettra pas de conserver des utilisateurs uniques.

1
Nelson La Rocca