0

I created an app that supports both Email/Password and Google authentication. I found that if I created an account in a first way, but logged out and in again with Google, the origin password was gone, and no way to sign in with email anymore. Is there any way to avoid so?

// Google authentication
const signInWithGoogle = useCallback(
      async event => {
        event.preventDefault();
        const provider = new firebase.auth.GoogleAuthProvider();
        try {
          await firebaseApp
          .auth()
          .signInWithRedirect(provider)
          .then(function(result) {
            var user = result.user.providerId;
            alert(user);
          });
          history.push("/transfer");
        } catch(error) {
          alert(error.message);
        }
      },
      [history]
    );
//Email/Password sign-in
const handleLogin = useCallback(
        async event => {
          event.preventDefault();
          const { email, password } = event.target.elements;
          try {
            await firebaseApp
              .auth()
              .signInWithEmailAndPassword(email.value, password.value)
              .then(function(result) {
                var user = result.user.providerId;
                alert(user);
              });
            history.push("/transfer");
          } catch (error) {
            alert(error);
          }
        },
        [history]
      );
// Email/Password sign-up
const handleSignUp = useCallback(async event => {
      event.preventDefault();
      const { email, password } = event.target.elements;
      try {
        await firebaseApp
          .auth()
          .createUserWithEmailAndPassword(email.value, password.value);
        history.push("/usersignupcred");
      } catch (error) {
        alert(error);
      }
    }, [history]);
Memphis Meng
  • 1,267
  • 2
  • 13
  • 34

1 Answers1

0

Here in the documentation you can see this explanation:

Note that some providers, such as Google and Microsoft, serve as both email and social identity providers. Email providers are considered authoritative for all addresses related to their hosted email domain. This means a user logging in with an email address hosted by the same provider will never raise this error (for example, signing in with Google using an @gmail.com email, or Microsoft using an @live.com or @outlook.com email).

I would recommend to use as similar approach like here from the docu:

// User tries to sign in with Facebook.
auth.signInWithPopup(new firebase.auth.FacebookAuthProvider()).catch(err => {
  // User's email already exists.
  if (err.code === 'auth/account-exists-with-different-credential') {
    // The pending Facebook credential.
    var pendingCred = err.credential;
    // The provider account's email address.
    var email = err.email;
    // Get the sign-in methods for this email.
    auth.fetchSignInMethodsForEmail(email).then(methods => {
      // If the user has several sign-in methods, the first method
      // in the list will be the "recommended" method to use.
      if (methods[0] === 'password') {
        // TODO: Ask the user for their password.
        // In real scenario, you should handle this asynchronously.
        var password = promptUserForPassword();
        auth.signInWithEmailAndPassword(email, password).then(result => {
          return result.user.linkWithCredential(pendingCred);
        }).then(() => {
          // Facebook account successfully linked to the existing user.
          goToApp();
        });
        return;
      }
      // All other cases are external providers.
      // Construct provider object for that provider.
      // TODO: Implement getProviderForProviderId.
      var provider = getProviderForProviderId(methods[0]);
      // At this point, you should let the user know that they already have an
      // account with a different provider, and validate they want to sign in
      // with the new provider.
      // Note: Browsers usually block popups triggered asynchronously, so in
      // real app, you should ask the user to click on a "Continue" button
      // that will trigger signInWithPopup().
      auth.signInWithPopup(provider).then(result => {
        // Note: Identity Platform doesn't control the provider's sign-in
        // flow, so it's possible for the user to sign in with an account
        // with a different email from the first one.

        // Link the Facebook credential. We have access to the pending
        // credential, so we can directly call the link method.
        result.user.linkWithCredential(pendingCred).then(usercred => {
          // Success.
          goToApp();
        });
      });
    });
  }
});

But instead of waiting for the error to be raised (none will be raised if using Google login as you also explained in your case) try always to call first fetchSignInMethodsForEmail and if the user has the email provider and tries now to use the Google one first log him in with the email provider and link him later with the Google provider.

Tarik Huber
  • 7,061
  • 2
  • 12
  • 18
  • I agree with you, it seems just weird that no error shows up when I expect it to do so. – Memphis Meng Jun 29 '21 at 00:35
  • However, there is a bug within your method: if I need to verify ahead of logging-in using `fetchSignInMethodForEmail`. I should have the email address first, which is not available until function `signInWithRedirect` or `signInWithPopUp` is executed. – Memphis Meng Jun 29 '21 at 01:03
  • It should be awailable before the signinwith popup. You just need to know the email of the user. – Tarik Huber Jun 29 '21 at 06:18
  • That's the question. I don't the email address of the person who is logging in until 3rd-party tells me. – Memphis Meng Jun 29 '21 at 13:43
  • I think that it's very good explained here: https://groups.google.com/g/firebase-talk/c/ms_NVQem_Cw/m/8g7BFk1IAAAJ Unfortunately you can either link the Google provider while the user is already signed in or ask fo the email before he goes to the google sign in (that would be a strange UX). Also related answer: https://stackoverflow.com/a/40768870/5519300 – Tarik Huber Jun 29 '21 at 20:43
  • I see! I guess I can only allow multiple accounts per email address, even though it could be a big trouble for me to link the user information together. – Memphis Meng Jun 29 '21 at 20:50
  • Unfortunately it is a strange behaviour from Firebase. You could maybe create a cloud function that would check for users with same email (asuming you enable multiple email signup) and try so merge them when the same email is used with another provider. – Tarik Huber Jun 29 '21 at 20:51
  • I think I kinda see another possibility of the reason why my codes didn't go as I expected: Google is a special case as it is a verified provider and will overwrite unverified accounts with the same email. So it is quite impossible to handle it using the your snippet or the example codes provided in Doc. Even Doc uses Facebook as the example. – Memphis Meng Jun 29 '21 at 22:27
  • 1
    That is what I try to explain. The only way to awoid overriding it is to ask first the user for email and use a port from my snipped and check what providers the user already has or not. As I sed before it's an ugly UX but unfortunately the only way I see to avoid the overriding or you enable multiple users with same email. – Tarik Huber Jun 30 '21 at 06:46