firebase-js-sdk icon indicating copy to clipboard operation
firebase-js-sdk copied to clipboard

Can't get credential from linking error / result as shown in docs

Open nadavhalfon opened this issue 2 years ago • 1 comments

[REQUIRED] Describe your environment

  • Operating System version: Mac OSX 12.4
  • Browser version: Chrome 104.0.5112.79 (arm64)
  • Firebase SDK version: 9.9.2
  • Firebase Product: auth

[REQUIRED] Describe the problem

After the users sign in successfully with email (google/facebook/apple) they are requested to enter their phone number, then we are verifying it using firebase auth and try to link the two accounts. If the phone number is not linked to any account everything works fine :) But if the phone number has firebase account then error is thrown with code auth/account-exists-with-different-credential, in that case we didn't find any solution other than that:

  1. trying to sign using the phone number
  2. delete the user
  3. then process again the phone number and try to link the accounts.

Worked according to this guide: https://firebase.google.com/docs/auth/web/account-linking#link-federated-auth-provider-credentials-to-a-user-account (The section where it says 'Account linking will fail if the credentials are already linked').

The problem is:

  1. credential object return null / credentialFromResult / credentialFromError - doesn't work
  2. I'm looking for a solution where the user doesn't need to receive extra OTPs

Steps to reproduce:

Try to get credential from an error or result after:

  1. linking error
  2. linking success

For example after linking the guide shows to use credentialFromResult and then perform new sign in - but the result has different field names that don't match the ones the function is looking for (idToken -> oauthIdToken) which causes an error

If you have any other suggestions for how to perform this logic, I'd love to hear them.

Relevant Code:

async verifyLoginCode(code: string) {
    this.isLoading = true;
    this.errorMassage = null;

    let phoneCredential = PhoneAuthProvider.credential(this.verificationId, code);

    this.tryToLinkWithCredential(phoneCredential)
      .catch(async (error: AuthError) => {
        if (error.code !== 'auth/account-exists-with-different-credential') {
          if (error.code === 'auth/provider-already-linked') {
            this.linkSucceeded();
          } else {
            this.linkError(error);
          }
        } else {
          try {
            const credential = OAuthProvider.credentialFromError(error);
            const phoneAuth = await signInWithCredential(this.auth, credential);
            await phoneAuth.user.delete();

            this.tryToLinkWithCredential(phoneCredential)
              .catch(async (error: AuthError) => {
                  this.linkError(error);
              });
          } catch (e) {
            this.linkError(e);
          }
        }
      });
  }

  async tryToLinkWithCredential(phoneCredential: AuthCredential) {
    return linkWithCredential(this.firebaseUser, phoneCredential)
      .then((linkResult) => {
        const credential = OAuthProvider.credentialFromResult(linkResult);
        // Sign in with the newly linked credential
        return signInWithCredential(this.auth, credential);
      })
      .then(() => {
        this.linkSucceeded();
      });
  }

I know that I can't use the same phone credential (it's like that because I thought it might work, then I understood it worked only because my phone was part of the testing numbers)

nadavhalfon avatar Aug 08 '22 13:08 nadavhalfon

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

google-oss-bot avatar Aug 08 '22 13:08 google-oss-bot

Hi @nadavhalfon, thanks for reaching out. Please let me know if I'm understanding this problem incorrectly--

It sounds like you're attempting to perform multi-factor sign in using linking? Is that correct? In that case you can use our MFA offering directly: https://firebase.google.com/docs/auth/web/multi-factor (note, you'll need to upgrade your project to Firebase Authentication with Identity Platform).

The reason your snippet isn't working is that the SMS codes are one-time only for security reasons. Once the code has been used once in order to attempt the link, that same code can't be used again afterwards.

sam-gc avatar Aug 12 '22 17:08 sam-gc

Hey, thanks for the answer I know it’s not supposed to work. I think the issue is not that, it’s the fact that linking two accounts that already exist is not supported, instead of linking error is thrown. Besides MFA, is there any alternative to link the two accounts using 1 SMS code?

nadavhalfon avatar Aug 13 '22 13:08 nadavhalfon

I see, yeah unfortunately there's no alternative. It was an explicit design decision that two existing accounts may not be linked. Think of "linking" as more like "adding a second way to sign in to this existing account." When attempting to link a new provider, if the credentials in question already belong to a different account, we will always throw an error (except in very particular cases involving unverified-emails and social providers).

Since we're not likely to change this behavior, I'm going to go ahead and close out this bug, but please feel free to respond with any other questions.

sam-gc avatar Aug 15 '22 15:08 sam-gc