Associer plusieurs fournisseurs à un compte

Ce document explique comment associer plusieurs fournisseurs à un seul compte Identity Platform.

Identity Platform utilise un identifiant unique pour identifier les utilisateurs. Cela permet aux utilisateurs de se connecter au même compte avec différents fournisseurs. Par exemple, un utilisateur qui s'est initialement enregistré avec un numéro de téléphone peut associer plus tard son compte Google, puis utiliser l'une des deux méthodes pour se connecter.

Avant de commencer

Ajoutez la compatibilité avec au moins deux fournisseurs d'identité à votre application.

Activer ou désactiver l'association de comptes

Le paramètre d'association de comptes détermine la façon dont Identity Platform gère les utilisateurs tentant de se connecter avec la même adresse e-mail via différents fournisseurs.

  • Associer des comptes qui utilisent la même adresse e-mail : Identity Platform génère une erreur si un utilisateur tente de se connecter avec une adresse e-mail déjà utilisée. Votre application peut détecter cette erreur et associer le nouveau fournisseur à son compte existant.

  • Créer plusieurs comptes pour chaque fournisseur d'identité : un nouveau compte utilisateur Identity Platform est créé chaque fois qu'un utilisateur se connecte avec un fournisseur différent.

Pour choisir un paramètre :

  1. Accédez à la page Paramètres d'Identity Platform dans la console Google Cloud.

    Accéder à la page Paramètres

  2. Sélectionnez un paramètre sous Association de comptes utilisateur.

  3. Cliquez sur Enregistrer.

Associer les identifiants d'un fournisseur fédéré

Pour associer les identifiants d'un fournisseur fédéré :

  1. Connectez-vous à l'utilisateur avec une méthode ou un fournisseur d'authentification.

  2. Récupérez l'objet de fournisseur qui correspond au fournisseur que vous souhaitez associer au compte de l'utilisateur. Exemple :

    Version 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();

    Version 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. Invitez l'utilisateur à se connecter avec le fournisseur que vous souhaitez associer. Vous pouvez soit ouvrir une fenêtre pop-up, soit rediriger la page actuelle. La redirection est plus facile pour les utilisateurs d'appareils mobiles.

    Pour afficher une fenêtre pop-up, appelez linkWithPopup() :

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

    Version Web 8

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

    Pour rediriger la page, commencez par appeler linkWithRedirect() :

    Suivez les bonnes pratiques lorsque vous utilisez signInWithRedirect, linkWithRedirect ou reauthenticateWithRedirect.

    Version Web 9

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

    Version Web 8

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

    Une fois l'utilisateur connecté, il est redirigé vers votre application. Ensuite, vous pouvez récupérer le résultat de la connexion en appelant getRedirectResult() :

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

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

Le compte de l'utilisateur avec le fournisseur fédéré est désormais associé à son compte Identity Platform, et l'utilisateur peut se connecter à l'aide du fournisseur.

Associer des identifiants de messagerie et de mot de passe

Pour ajouter une adresse e-mail et un mot de passe à un compte utilisateur existant, procédez comme suit :

  1. Connectez-vous à l'utilisateur avec une méthode ou un fournisseur d'identité.

  2. Demandez à l'utilisateur de spécifier une adresse e-mail et un mot de passe.

  3. Créez un objet AuthCredential avec l'adresse e-mail et le mot de passe :

    Version Web 9

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

    Version Web 8

    var credential = firebase.auth.EmailAuthProvider.credential(email, password);
  4. Transmettez l'objet AuthCredential à la méthode linkWithCredential() sur l'utilisateur connecté :

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

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

Les identifiants d'adresse e-mail et de mot de passe sont désormais associés au compte Identity Platform de l'utilisateur, et celui-ci peut s'en servir pour se connecter.

Notez qu'un identifiant de fournisseur fédéré peut être associé à une adresse e-mail/mot de passe associée à une autre adresse e-mail. Dans ce cas, l'adresse e-mail correspondant au fournisseur fédéré peut être utilisée pour créer un compte de messagerie/mot de passe distinct.

Gérer les erreurs de compte existant avec un identifiant différent

Si vous avez activé le paramètre Associer des comptes qui utilisent la même adresse e-mail dans la console Google Cloud, lorsqu'un utilisateur tente de se connecter à un fournisseur (tel que SAML) avec une adresse e-mail qui existe déjà pour un autre fournisseur (tel que Google), l'erreur auth/account-exists-with-different-credential est générée (avec un objet AuthCredential).

Pour gérer cette erreur, invitez l'utilisateur à se connecter avec le fournisseur existant. Appelez ensuite linkWithCredential(), linkWithPopup() ou linkWithRedirect() pour associer le nouveau fournisseur à son compte à l'aide de AuthCredential.

L'exemple suivant montre comment gérer cette erreur lorsqu'un utilisateur tente de se connecter à l'aide de Facebook :

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

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

L'utilisation d'une redirection est semblable à une fenêtre pop-up, à la différence que vous devez mettre en cache l'identifiant en attente entre les redirections de pages (par exemple, en utilisant l'espace de stockage de session).

Notez que certains fournisseurs, tels que Google et Microsoft, servent à la fois de fournisseurs de messagerie et d'identité sociale. Les fournisseurs de messagerie sont considérés comme faisant autorité pour toutes les adresses associées à leur domaine de messagerie hébergé. Cela signifie qu'un utilisateur qui se connecte avec une adresse e-mail hébergée par le même fournisseur ne génère jamais cette erreur (par exemple, se connecter à Google en utilisant une adresse e-mail @gmail.com, ou à Microsoft en utilisant une adresse e-mail @live.com ou @outlook.com.)

Fusionner des comptes manuellement

Si un utilisateur tente de se connecter en utilisant le même fournisseur avec des identifiants déjà associés à un autre compte utilisateur, les méthodes intégrées du SDK client pour l'association de comptes échouent. Dans ce cas, vous devez fusionner les comptes manuellement, puis supprimer le deuxième compte. Exemple :

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

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

Vous pouvez dissocier un fournisseur du compte d'un utilisateur. L'utilisateur ne pourra plus s'authentifier avec ce fournisseur.

Pour dissocier un fournisseur, transmettez l'ID du fournisseur à la méthode unlink(). Vous pouvez obtenir les ID des fournisseurs d'authentification associés à un utilisateur à partir de la propriété providerData.

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

Version Web 8

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

Étapes suivantes