Associar vários fornecedores a uma conta

Este documento mostra como associar vários fornecedores a uma única conta do Identity Platform.

A Identity Platform usa um ID exclusivo para identificar os utilizadores. Isto permite que os utilizadores iniciem sessão na mesma conta com diferentes fornecedores. Por exemplo, um utilizador que se registou inicialmente com um número de telefone pode associar posteriormente a sua Conta Google e, em seguida, utilizar qualquer um dos métodos para iniciar sessão.

Antes de começar

Adicione suporte para dois ou mais fornecedores de identidade à sua app.

Ativar ou desativar a associação de contas

A definição de associação de contas determina como a Identity Platform processa os utilizadores que tentam iniciar sessão com o mesmo email através de diferentes fornecedores.

  • Associe contas que usam o mesmo email: a Identity Platform gera um erro se um utilizador tentar iniciar sessão com um email que já está a ser usado. A sua app pode detetar este erro e associar o novo fornecedor à conta existente.

  • Crie várias contas para cada fornecedor de identidade: é criada uma nova conta de utilizador da Identity Platform sempre que um utilizador inicia sessão com um fornecedor diferente.

Para escolher uma definição:

  1. Aceda à página Definições do Identity Platform na Google Cloud consola.

    Aceda à página Definições

  2. Selecione uma definição em Associação de contas de utilizador.

  3. Clique em Guardar.

Associar credenciais de fornecedor federado

Para associar credenciais de um fornecedor federado:

  1. Inicie sessão no utilizador com qualquer fornecedor ou método de autenticação.

  2. Obtenha o objeto do fornecedor que corresponde ao fornecedor que quer associar à conta do utilizador. Por exemplo:

    Versão Web 9

    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 Web 8

    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. Peça ao utilizador para iniciar sessão com o fornecedor que quer associar. Pode abrir uma janela de pop-up ou redirecionar a página atual. O redirecionamento é mais fácil para os utilizadores em dispositivos móveis.

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

    Versão Web 9

    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 Web 8

    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, chame primeiro linkWithRedirect():

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

    Versão Web 9

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

    Versão Web 8

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

    Depois de o utilizador iniciar sessão, é redirecionado de volta para a sua app. Em seguida, pode obter o resultado do início de sessão chamando getRedirectResult():

    Versão Web 9

    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 Web 8

    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 utilizador com o fornecedor federado está agora associada à respetiva conta do Identity Platform, e o utilizador pode usar o fornecedor para iniciar sessão.

Associar credenciais de email e palavra-passe

Para adicionar um endereço de email e uma palavra-passe a uma conta de utilizador existente:

  1. Inicie sessão do utilizador com qualquer fornecedor de identidade ou método.

  2. Pedir ao utilizador um endereço de email e uma palavra-passe.

  3. Crie um objeto AuthCredential com o endereço de email e a palavra-passe:

    Versão Web 9

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

    Versão Web 8

    var credential = firebase.auth.EmailAuthProvider.credential(email, password);
  4. Transmita o objeto AuthCredential ao método linkWithCredential() no utilizador com sessão iniciada:

    Versão Web 9

    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 Web 8

    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 email e palavra-passe estão agora associadas à conta do Identity Platform do utilizador, e este pode usá-las para iniciar sessão.

Tenha em atenção que uma credencial de fornecedor federado pode ser associada a uma conta de email/palavra-passe com um email diferente. Se isto acontecer, pode usar o email correspondente ao fornecedor federado para criar uma conta de email/palavra-passe separada.

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

Se ativou a definição Associar contas que usam o mesmo email na Google Cloud consola, quando um utilizador tenta iniciar sessão num fornecedor (como SAML) com um email que já existe para outro fornecedor (como Google), é apresentado o erro auth/account-exists-with-different-credential (juntamente com um objeto AuthCredential).

Para resolver este erro, peça ao utilizador para iniciar sessão com o fornecedor existente. Em seguida, ligue para linkWithCredential(), linkWithPopup() ou linkWithRedirect() para associar o novo fornecedor à respetiva conta através do AuthCredential.

O exemplo seguinte mostra como processar este erro quando um utilizador tenta iniciar sessão através do Facebook:

Versão Web 9

  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 Web 8

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

A utilização de um redirecionamento é semelhante à de uma janela de pop-up, exceto que tem de colocar em cache a credencial pendente entre redirecionamentos de páginas (por exemplo, através do armazenamento de sessões).

Tenha em atenção que alguns fornecedores, como a Google e a Microsoft, funcionam como fornecedores de email e identidade social. Os fornecedores de email são considerados fiáveis para todos os endereços relacionados com o respetivo domínio de email alojado. Isto significa que um utilizador que inicie sessão com um endereço de email alojado pelo mesmo fornecedor nunca vai gerar este erro (por exemplo, iniciar sessão com o Google através de um email @gmail.com ou com a Microsoft através de um email @live.com ou @outlook.com).

Unir contas manualmente

Se um utilizador tentar iniciar sessão com credenciais já associadas a outra conta de utilizador através do mesmo fornecedor, os métodos incorporados do SDK do cliente para associação de contas falham. Nesta situação, tem de unir as contas manualmente e, em seguida, eliminar a segunda conta. Por exemplo:

Versão Web 9

// 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 Web 8

// 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);
}

Pode desassociar um fornecedor da conta de um utilizador. O utilizador deixa de poder autenticar-se com esse fornecedor.

Para desassociar um fornecedor, transmita o ID do fornecedor ao método unlink(). Pode obter os IDs dos fornecedores de autenticação associados a um utilizador a partir da propriedade providerData.

Versão Web 9

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 Web 8

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

O que se segue?