Como vincular vários provedores a uma conta

Neste documento, você verá como vincular vários provedores a uma única conta do Identity Platform.

O Identity Platform usa um ID exclusivo para identificar usuários. Isso permite que os usuários façam login na mesma conta com provedores diferentes. Por exemplo, um usuário que inicialmente se registra com um número de telefone pode vincular a Conta do Google dele posteriormente e usar um dos métodos para fazer login.

Antes de começar

Adicione ao app o suporte para dois ou mais provedores de identidade.

Como ativar ou desativar a vinculação de contas

A configuração da vinculação de conta determina como o Identity Platform lida com os usuários que tentam fazer login com o mesmo e-mail usando diferentes provedores.

  • Contas vinculadas que usam o mesmo e-mail: o Identity Platform gerará um erro caso um usuário tente fazer login com um e-mail que já esteja em uso. O app pode identificar esse erro e vincular o novo provedor à conta atual.

  • Criar várias contas para cada provedor de identidade: uma nova conta de usuário do Identity Platform será criada sempre que um usuário fizer login com um provedor diferente.

Para escolher uma configuração:

  1. Acesse a página Configurações do Identity Platform no Console do Google Cloud.

    Ir para a página "Configurações"

  2. Selecione uma configuração em Vinculação da conta do usuário.

  3. Clique em Salvar.

Como vincular credenciais de provedores federados

Para vincular credenciais de um provedor federado:

  1. Faça login do usuário com um provedor ou método de autenticação.

  2. Consiga o objeto do provedor que corresponde ao provedor que você quer vincular à conta do usuário. Exemplo:

    Versão 9 para a Web

    import { GoogleAuthProvider, FacebookAuthProvider, TwitterAuthProvider, GithubAuthProvider } from "firebase/auth";
    
    const googleProvider = new GoogleAuthProvider();
    const facebookProvider = new FacebookAuthProvider();
    const twitterProvider = new TwitterAuthProvider();
    const githubProvider = new GithubAuthProvider();

    Versão 8 para a Web

    var googleProvider = new firebase.auth.GoogleAuthProvider();
    var facebookProvider = new firebase.auth.FacebookAuthProvider();
    var twitterProvider = new firebase.auth.TwitterAuthProvider();
    var githubProvider = new firebase.auth.GithubAuthProvider();
  3. Solicite que o usuário faça login com o provedor que você quer vincular. Você pode abrir uma janela pop-up ou redirecionar a página atual. O redirecionamento é mais fácil para usuários de dispositivos móveis.

    Para mostrar um pop-up, chame linkWithPopup():

    Versão 9 para a Web

    import { getAuth, linkWithPopup, GoogleAuthProvider } from "firebase/auth";
    const provider = new GoogleAuthProvider();
    
    const auth = getAuth();
    linkWithPopup(auth.currentUser, provider).then((result) => {
      // Accounts successfully linked.
      const credential = GoogleAuthProvider.credentialFromResult(result);
      const user = result.user;
      // ...
    }).catch((error) => {
      // Handle Errors here.
      // ...
    });

    Versão 8 para a Web

    auth.currentUser.linkWithPopup(provider).then((result) => {
      // Accounts successfully linked.
      var credential = result.credential;
      var user = result.user;
      // ...
    }).catch((error) => {
      // Handle Errors here.
      // ...
    });

    Para redirecionar a página, primeiro chame linkWithRedirect():

    Siga as práticas recomendadas ao usar signInWithRedirect, linkWithRedirect ou reauthenticateWithRedirect.

    Versão 9 para a Web

    import { getAuth, linkWithRedirect, GoogleAuthProvider } from "firebase/auth";
    const provider = new GoogleAuthProvider();
    
    const auth = getAuth();
    linkWithRedirect(auth.currentUser, provider)
      .then(/* ... */)
      .catch(/* ... */);

    Versão 8 para a Web

    auth.currentUser.linkWithRedirect(provider)
      .then(/* ... */)
      .catch(/* ... */);

    Depois que o usuário fizer login, ele será redirecionado ao seu app. Em seguida, recupere o resultado do login chamando getRedirectResult():

    Versão 9 para a Web

    import { getRedirectResult } from "firebase/auth";
    getRedirectResult(auth).then((result) => {
      const credential = GoogleAuthProvider.credentialFromResult(result);
      if (credential) {
        // Accounts successfully linked.
        const user = result.user;
        // ...
      }
    }).catch((error) => {
      // Handle Errors here.
      // ...
    });

    Versão 8 para a Web

    auth.getRedirectResult().then((result) => {
      if (result.credential) {
        // Accounts successfully linked.
        var credential = result.credential;
        var user = result.user;
        // ...
      }
    }).catch((error) => {
      // Handle Errors here.
      // ...
    });

A conta do usuário com o provedor federado agora está vinculada à conta do Identity Platform e o provedor pode ser usado para fazer login.

Como vincular credenciais de e-mail e senha

Para adicionar um endereço de e-mail e uma senha a uma conta de usuário atual:

  1. Faça login do usuário com um provedor ou método de identidade.

  2. Solicite ao usuário um endereço de e-mail e uma senha.

  3. Crie um objeto AuthCredential com o endereço de e-mail e a senha:

    Versão 9 para a Web

    import { EmailAuthProvider } from "firebase/auth";
    
    const credential = EmailAuthProvider.credential(email, password);

    Versão 8 para a Web

    var credential = firebase.auth.EmailAuthProvider.credential(email, password);
  4. Transmita o objeto AuthCredential para o método linkWithCredential() no usuário conectado:

    Versão 9 para a Web

    import { getAuth, linkWithCredential } from "firebase/auth";
    
    const auth = getAuth();
    linkWithCredential(auth.currentUser, credential)
      .then((usercred) => {
        const user = usercred.user;
        console.log("Account linking success", user);
      }).catch((error) => {
        console.log("Account linking error", error);
      });

    Versão 8 para a Web

    auth.currentUser.linkWithCredential(credential)
      .then((usercred) => {
        var user = usercred.user;
        console.log("Account linking success", user);
      }).catch((error) => {
        console.log("Account linking error", error);
      });

As credenciais de e-mail e senha agora estão vinculadas à conta do Identity Platform do usuário e podem usá-las para fazer login.

Uma credencial de provedor federado pode ser vinculada a uma conta de e-mail/senha com um e-mail diferente. Caso isso aconteça, o e-mail correspondente ao provedor federado pode ser usado para criar uma conta de e-mail/senha separada.

Como processar o erro account-exists-with-different-credential

Se você ativou a configuração Vincular contas que usam o mesmo e-mail no console do Google Cloud, quando um usuário tentar fazer login em um provedor (como SAML) com um e-mail que já existe para outro provedor (como o Google), o erro auth/account-exists-with-different-credential será gerado (com um objeto AuthCredential).

Para lidar com esse erro, solicite que o usuário faça login com o provedor existente. Em seguida, chame linkWithCredential(), linkWithPopup() ou linkWithRedirect() para associar o novo provedor à conta usando o AuthCredential.

O exemplo a seguir mostra como tratar esse erro quando um usuário tenta fazer login usando o Facebook:

Versão 9 para a Web

  import { signInWithPopup, signInWithEmailAndPassword, linkWithCredential } from "firebase/auth";

  // User tries to sign in with Facebook.
  signInWithPopup(auth, facebookProvider).catch((error) => {
  // User's email already exists.
  if (error.code === 'auth/account-exists-with-different-credential') {
    // The pending Facebook credential.
    const pendingCred = error.credential;
    // The provider account's email address.
    const email = error.customData.email;

    // Present the user with a list of providers they might have
    // used to create the original account.
    // Then, ask the user to sign in with the existing provider.
    const method = promptUserForSignInMethod();

    if (method === 'password') {
      // TODO: Ask the user for their password.
      // In real scenario, you should handle this asynchronously.
      const password = promptUserForPassword();
      signInWithEmailAndPassword(auth, email, password).then((result) => {
        return linkWithCredential(result.user, 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.
    const provider = getProviderForProviderId(method);
    // 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().
    signInWithPopup(auth, 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.
      linkWithCredential(result.user, pendingCred).then((userCred) => {
        // Success.
        goToApp();
      });
    });
  }
});

Versão 8 para a Web

  // User tries to sign in with Facebook.
      auth.signInWithPopup(facebookProvider).catch((error) => {
  // User's email already exists.
  if (error.code === 'auth/account-exists-with-different-credential') {
    // The pending Facebook credential.
    const pendingCred = error.credential;
    // The provider account's email address.
    const email = error.email;

    // Present the user with a list of providers they might have
    // used to create the original account.
    // Then, ask the user to sign in with the existing provider.
    const method = promptUserForSignInMethod();

    if (method === 'password') {
      // TODO: Ask the user for their password.
      // In real scenario, you should handle this asynchronously.
      const 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.
    const provider = getProviderForProviderId(method);
    // 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();
      });
    });
  }
});

O uso de um redirecionamento é semelhante a um pop-up, mas você precisa armazenar em cache a credencial pendente entre redirecionamentos de página (por exemplo, usando o armazenamento de sessão).

Alguns provedores, como o Google e a Microsoft, servem como provedores de e-mail e identidades sociais. Os provedores de e-mail são considerados autoritativos para todos os endereços relacionados ao domínio de e-mail hospedado. Isso significa que um usuário que fizer login com um endereço de e-mail hospedado pelo mesmo provedor nunca gerará esse erro (por exemplo, fazer login com o Google usando um @gmail.com e-mail ou Microsoft usando um @live.com, @outlook.com e-mail).

Como mesclar contas manualmente

Se um usuário tentar fazer login com credenciais que já estão vinculadas a outra conta de usuário usando o mesmo provedor, os métodos integrados do SDK do cliente para vinculação de contas falharão. Nessa situação, você precisará mesclar as contas manualmente e excluir a segunda conta. Exemplo:

Versão 9 para a Web

// Sign in first account.
const result1 = await signInWithCredential(auth, cred1);
const user1 = result1.user;
// Try to link a credential that belongs to an existing account
try {
  await linkWithCredential(user1, cred2);
} catch (error) {
  // cred2 already exists so an error is thrown.
  const result2 = await signInWithCredential(auth, error.credential);
  const user2 = result2.user;
  // Merge the data.
  mergeData(user1, user2);
  // Delete one of the accounts, and try again.
  await user2.delete();
  // Linking now will work.
  await linkWithCredential(user1, result2.credential);
}

Versão 8 para a Web

// Sign in first account.
const result1 = await auth.signInWithCredential(cred1);
const user1 = result1.user;
// Try to link a credential that belongs to an existing account
try {
  await user1.linkWithCredential(cred2);
} catch (error) {
  // cred2 already exists so an error is thrown.
  const result2 = await auth.signInWithCredential(error.credential);
  const user2 = result2.user;
  // Merge the data.
  mergeData(user1, user2);
  // Delete one of the accounts, and try again.
  await user2.delete();
  // Linking now will work.
  await user1.linkWithCredential(result2.credential);
}

É possível desvincular um provedor da conta de um usuário. O usuário não poderá mais se autenticar com esse provedor.

Para desvincular um provedor, transmita o ID dele ao método unlink(). Consiga esses IDs dos provedores de autenticação vinculados a um usuário usando a propriedade providerData.

Versão 9 para a Web

import { getAuth, unlink } from "firebase/auth";

const auth = getAuth();
unlink(auth.currentUser, providerId).then(() => {
  // Auth provider unlinked from account
  // ...
}).catch((error) => {
  // An error happened
  // ...
});

Versão 8 para a Web

user.unlink(providerId).then(() => {
  // Auth provider unlinked from account
  // ...
}).catch((error) => {
  // An error happened
  // ...
});

A seguir