Catch up on highlights from Firebase at Google I/O 2023. Learn more

Ajoutez une authentification multifacteur à votre application Web

Si vous avez effectué la mise à niveau vers Firebase Authentication with Identity Platform, vous pouvez ajouter l'authentification multifacteur SMS à votre application Web.

L'authentification multifacteur augmente la sécurité de votre application. Alors que les attaquants compromettent souvent les mots de passe et les comptes sociaux, l'interception d'un message texte est plus difficile.

Avant que tu commences

  1. Activez au moins un fournisseur prenant en charge l'authentification multifacteur. Chaque fournisseur prend en charge MFA, à l'exception de l'authentification par téléphone, de l'authentification anonyme et d'Apple Game Center.

  2. Assurez-vous que votre application vérifie les e-mails des utilisateurs. MFA nécessite une vérification par e-mail. Cela empêche les acteurs malveillants de s'inscrire à un service avec un e-mail qu'ils ne possèdent pas, puis de verrouiller le véritable propriétaire en ajoutant un deuxième facteur.

Utiliser la multilocation

Si vous activez l'authentification multifacteur pour une utilisation dans un environnement mutualisé , assurez-vous de suivre les étapes suivantes (en plus du reste des instructions de ce document) :

  1. Dans la console GCP, sélectionnez le locataire avec lequel vous souhaitez travailler.

  2. Dans votre code, définissez le champ tenantId de l'instance Auth sur l'ID de votre locataire. Par exemple:

    API Web modulaire

    import { getAuth } from "firebase/auth";
    
    const auth = getAuth(app);
    auth.tenantId = "myTenantId1";
    

    API d'espace de noms Web

    firebase.auth().tenantId = 'myTenantId1';
    

Activer l'authentification multifacteur

  1. Ouvrez la page Authentification > Méthode de connexion de la console Firebase.

  2. Dans la section Avancé , activez Authentification multifacteur SMS .

    Vous devez également entrer les numéros de téléphone avec lesquels vous testerez votre application. Bien que facultatif, l'enregistrement des numéros de téléphone de test est fortement recommandé pour éviter les limitations pendant le développement.

  3. Si vous n'avez pas encore autorisé le domaine de votre application, ajoutez-le à la liste verte sur la page Authentification > Paramètres de la console Firebase.

Choisir un modèle d'inscription

Vous pouvez choisir si votre application requiert une authentification multifacteur, et comment et quand inscrire vos utilisateurs. Certains modèles courants incluent :

  • Inscrivez le deuxième facteur de l'utilisateur dans le cadre de l'inscription. Utilisez cette méthode si votre application nécessite une authentification multifacteur pour tous les utilisateurs.

  • Offrez une option désactivable pour inscrire un deuxième facteur lors de l'inscription. Les applications qui souhaitent encourager, mais pas exiger, l'authentification multifacteur peuvent préférer cette approche.

  • Offrez la possibilité d'ajouter un deuxième facteur à partir de la page de gestion du compte ou du profil de l'utilisateur, au lieu de l'écran d'inscription. Cela minimise les frictions pendant le processus d'inscription, tout en rendant l'authentification multifacteur disponible pour les utilisateurs sensibles à la sécurité.

  • Nécessite l'ajout progressif d'un deuxième facteur lorsque l'utilisateur souhaite accéder à des fonctionnalités avec des exigences de sécurité accrues.

Configurer le vérificateur reCAPTCHA

Avant de pouvoir envoyer des codes SMS, vous devez configurer un vérificateur reCAPTCHA. Firebase utilise reCAPTCHA pour empêcher les abus en s'assurant que les demandes de vérification de numéro de téléphone proviennent de l'un des domaines autorisés de votre application.

Vous n'avez pas besoin de configurer manuellement un client reCAPTCHA ; l'objet RecaptchaVerifier du SDK client crée et initialise automatiquement les clés et secrets client nécessaires.

Utiliser un reCAPTCHA invisible

L'objet RecaptchaVerifier prend en charge le reCAPTCHA invisible , qui peut souvent vérifier l'utilisateur sans nécessiter aucune interaction. Pour utiliser un reCAPTCHA invisible, créez un RecaptchaVerifier avec le paramètre size défini sur invisible et spécifiez l'ID de l'élément d'interface utilisateur qui démarre l'inscription multifacteur :

API Web modulaire

import { RecaptchaVerifier } from "firebase/auth";

const recaptchaVerifier = new RecaptchaVerifier("sign-in-button", {
    "size": "invisible",
    "callback": function(response) {
        // reCAPTCHA solved, you can proceed with
        // phoneAuthProvider.verifyPhoneNumber(...).
        onSolvedRecaptcha();
    }
}, auth);

API d'espace de noms Web

var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': function(response) {
  // reCAPTCHA solved, you can proceed with phoneAuthProvider.verifyPhoneNumber(...).
  onSolvedRecaptcha();
}
});

Utiliser le widget reCAPTCHA

Pour utiliser un widget reCAPTCHA visible, créez un élément HTML pour contenir le widget, puis créez un objet RecaptchaVerifier avec l'ID du conteneur d'interface utilisateur. Vous pouvez également éventuellement définir des rappels qui sont invoqués lorsque le reCAPTCHA est résolu ou expire :

API Web modulaire

import { RecaptchaVerifier } from "firebase/auth";

const recaptchaVerifier = new RecaptchaVerifier(
    "recaptcha-container",

    // Optional reCAPTCHA parameters.
    {
      "size": "normal",
      "callback": function(response) {
        // reCAPTCHA solved, you can proceed with 
        // phoneAuthProvider.verifyPhoneNumber(...).
        onSolvedRecaptcha();
      },
      "expired-callback": function() {
        // Response expired. Ask user to solve reCAPTCHA again.
        // ...
      }
    }, auth
);

API d'espace de noms Web

var recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
  'recaptcha-container',
  // Optional reCAPTCHA parameters.
  {
    'size': 'normal',
    'callback': function(response) {
      // reCAPTCHA solved, you can proceed with phoneAuthProvider.verifyPhoneNumber(...).
      // ...
      onSolvedRecaptcha();
    },
    'expired-callback': function() {
      // Response expired. Ask user to solve reCAPTCHA again.
      // ...
    }
  });

Pré-rendu du reCAPTCHA

Si vous le souhaitez, vous pouvez pré-afficher le reCAPTCHA avant de commencer l'inscription à deux facteurs :

API Web modulaire

recaptchaVerifier.render()
    .then(function (widgetId) {
        window.recaptchaWidgetId = widgetId;
    });

API d'espace de noms Web

recaptchaVerifier.render()
  .then(function(widgetId) {
    window.recaptchaWidgetId = widgetId;
  });

Une fois render() résolu, vous obtenez l'ID du widget reCAPTCHA, que vous pouvez utiliser pour effectuer des appels à l' API reCAPTCHA :

var recaptchaResponse = grecaptcha.getResponse(window.recaptchaWidgetId);

RecaptchaVerifier fait abstraction de cette logique avec la méthode de vérification , vous n'avez donc pas besoin de gérer directement la variable grecaptcha .

Inscrire un deuxième facteur

Pour inscrire un nouveau facteur secondaire pour un utilisateur :

  1. Réauthentifiez l'utilisateur.

  2. Demandez à l'utilisateur d'entrer son numéro de téléphone.

  3. Initialisez le vérificateur reCAPTCHA comme illustré dans la section précédente. Ignorez cette étape si une instance RecaptchaVerifier est déjà configurée :

    API Web modulaire

    import { RecaptchaVerifier } from "firebase/auth";
    
    const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
    

    API d'espace de noms Web

    var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
    
  4. Obtenez une session multi-facteurs pour l'utilisateur :

    API Web modulaire

    import { multiFactor } from "firebase/auth";
    
    multiFactor(user).getSession().then(function (multiFactorSession) {
        // ...
    });
    

    API d'espace de noms Web

    user.multiFactor.getSession().then(function(multiFactorSession) {
      // ...
    })
    
  5. Initialisez un objet PhoneInfoOptions avec le numéro de téléphone de l'utilisateur et la session multifacteur :

    API Web modulaire

    // Specify the phone number and pass the MFA session.
    const phoneInfoOptions = {
      phoneNumber: phoneNumber,
      session: multiFactorSession
    };
    

    API d'espace de noms Web

    // Specify the phone number and pass the MFA session.
    var phoneInfoOptions = {
      phoneNumber: phoneNumber,
      session: multiFactorSession
    };
    
  6. Envoyez un message de vérification au téléphone de l'utilisateur :

    API Web modulaire

    import { PhoneAuthProvider } from "firebase/auth";
    
    const phoneAuthProvider = new PhoneAuthProvider(auth);
    phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
        .then(function (verificationId) {
            // verificationId will be needed to complete enrollment.
        });
    

    API d'espace de noms Web

    var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
    // Send SMS verification code.
    return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
      .then(function(verificationId) {
        // verificationId will be needed for enrollment completion.
      })
    

    Bien que cela ne soit pas obligatoire, il est recommandé d'informer les utilisateurs à l'avance qu'ils recevront un SMS et que les tarifs standard s'appliquent.

  7. Si la demande échoue, réinitialisez le reCAPTCHA, puis répétez l'étape précédente pour que l'utilisateur puisse réessayer. Notez que verifyPhoneNumber() réinitialisera automatiquement le reCAPTCHA lorsqu'il génère une erreur, car les jetons reCAPTCHA sont à usage unique.

    API Web modulaire

    recaptchaVerifier.clear();
    

    API d'espace de noms Web

    recaptchaVerifier.clear();
    
  8. Une fois le code SMS envoyé, demandez à l'utilisateur de vérifier le code :

    API Web modulaire

    // Ask user for the verification code. Then:
    const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
    

    API d'espace de noms Web

    // Ask user for the verification code. Then:
    var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
    
  9. Initialisez un objet MultiFactorAssertion avec PhoneAuthCredential :

    API Web modulaire

    import { PhoneMultiFactorGenerator } from "firebase/auth";
    
    const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
    

    API d'espace de noms Web

    var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
    
  10. Complétez l'inscription. Si vous le souhaitez, vous pouvez spécifier un nom d'affichage pour le second facteur. Ceci est utile pour les utilisateurs avec plusieurs seconds facteurs, car le numéro de téléphone est masqué pendant le flux d'authentification (par exemple, +1******1234).

    API Web modulaire

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    multiFactor(user).enroll(multiFactorAssertion, "My personal phone number");
    

    API d'espace de noms Web

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user.multiFactor.enroll(multiFactorAssertion, 'My personal phone number');
    

Le code ci-dessous montre un exemple complet d'inscription d'un deuxième facteur :

API Web modulaire

import {
    multiFactor, PhoneAuthProvider, PhoneMultiFactorGenerator,
    RecaptchaVerifier
} from "firebase/auth";

const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
multiFactor(user).getSession()
    .then(function (multiFactorSession) {
        // Specify the phone number and pass the MFA session.
        const phoneInfoOptions = {
            phoneNumber: phoneNumber,
            session: multiFactorSession
        };

        const phoneAuthProvider = new PhoneAuthProvider(auth);

        // Send SMS verification code.
        return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier);
    }).then(function (verificationId) {
        // Ask user for the verification code. Then:
        const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
        const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);

        // Complete enrollment.
        return multiFactor(user).enroll(multiFactorAssertion, mfaDisplayName);
    });

API d'espace de noms Web

var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
user.multiFactor.getSession().then(function(multiFactorSession) {
  // Specify the phone number and pass the MFA session.
  var phoneInfoOptions = {
    phoneNumber: phoneNumber,
    session: multiFactorSession
  };
  var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
  // Send SMS verification code.
  return phoneAuthProvider.verifyPhoneNumber(
      phoneInfoOptions, recaptchaVerifier);
})
.then(function(verificationId) {
  // Ask user for the verification code.
  var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
  var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
  // Complete enrollment.
  return user.multiFactor.enroll(multiFactorAssertion, mfaDisplayName);
});

Toutes nos félicitations! Vous avez enregistré avec succès un deuxième facteur d'authentification pour un utilisateur.

Connexion des utilisateurs avec un deuxième facteur

Pour connecter un utilisateur avec la vérification par SMS à deux facteurs :

  1. Connectez l'utilisateur avec son premier facteur, puis détectez l'erreur auth/multi-factor-auth-required . Cette erreur contient un résolveur, des conseils sur les seconds facteurs inscrits et une session sous-jacente prouvant que l'utilisateur s'est authentifié avec succès avec le premier facteur.

    Par exemple, si le premier facteur de l'utilisateur était une adresse e-mail et un mot de passe :

    API Web modulaire

    import { getAuth, getMultiFactorResolver} from "firebase/auth";
    
    const auth = getAuth();
    signInWithEmailAndPassword(auth, email, password)
        .then(function (userCredential) {
            // User successfully signed in and is not enrolled with a second factor.
        })
        .catch(function (error) {
            if (error.code == 'auth/multi-factor-auth-required') {
                // The user is a multi-factor user. Second factor challenge is required.
                resolver = getMultiFactorResolver(auth, error);
                // ...
            } else if (error.code == 'auth/wrong-password') {
                // Handle other errors such as wrong password.
            }
    });
    

    API d'espace de noms Web

    firebase.auth().signInWithEmailAndPassword(email, password)
      .then(function(userCredential) {
        // User successfully signed in and is not enrolled with a second factor.
      })
      .catch(function(error) {
        if (error.code == 'auth/multi-factor-auth-required') {
          // The user is a multi-factor user. Second factor challenge is required.
          resolver = error.resolver;
          // ...
        } else if (error.code == 'auth/wrong-password') {
          // Handle other errors such as wrong password.
        } ...
      });
    

    Si le premier facteur de l'utilisateur est un fournisseur fédéré, tel que OAuth, SAML ou OIDC, interceptez l'erreur après avoir appelé signInWithPopup() ou signInWithRedirect() .

  2. Si l'utilisateur a plusieurs facteurs secondaires inscrits, demandez-lui lequel utiliser :

    API Web modulaire

    // Ask user which second factor to use.
    // You can get the masked phone number via resolver.hints[selectedIndex].phoneNumber
    // You can get the display name via resolver.hints[selectedIndex].displayName
    
    if (resolver.hints[selectedIndex].factorId === PhoneMultiFactorGenerator.FACTOR_ID) {
        // User selected a phone second factor.
        // ...
    } else {
        // Unsupported second factor.
        // Note that only phone second factors are currently supported.
    }
    

    API d'espace de noms Web

    // Ask user which second factor to use.
    // You can get the masked phone number via resolver.hints[selectedIndex].phoneNumber
    // You can get the display name via resolver.hints[selectedIndex].displayName
    if (resolver.hints[selectedIndex].factorId === firebase.auth.PhoneMultiFactorGenerator.FACTOR_ID) {
      // User selected a phone second factor.
      // ...
    } else {
      // Unsupported second factor.
      // Note that only phone second factors are currently supported.
    }
    
  3. Initialisez le vérificateur reCAPTCHA comme illustré dans la section précédente. Ignorez cette étape si une instance RecaptchaVerifier est déjà configurée :

    API Web modulaire

    import { RecaptchaVerifier } from "firebase/auth";
    
    recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
    

    API d'espace de noms Web

    var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
    
  4. Initialisez un objet PhoneInfoOptions avec le numéro de téléphone de l'utilisateur et la session multifacteur. Ces valeurs sont contenues dans l'objet resolver transmis à l'erreur auth/multi-factor-auth-required :

    API Web modulaire

    const phoneInfoOptions = {
        multiFactorHint: resolver.hints[selectedIndex],
        session: resolver.session
    };
    

    API d'espace de noms Web

    var phoneInfoOptions = {
      multiFactorHint: resolver.hints[selectedIndex],
      session: resolver.session
    };
    
  5. Envoyez un message de vérification au téléphone de l'utilisateur :

    API Web modulaire

    // Send SMS verification code.
    const phoneAuthProvider = new PhoneAuthProvider(auth);
    phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
        .then(function (verificationId) {
            // verificationId will be needed for sign-in completion.
        });
    

    API d'espace de noms Web

    var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
    // Send SMS verification code.
    return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
      .then(function(verificationId) {
        // verificationId will be needed for sign-in completion.
      })
    
  6. Si la requête échoue, réinitialisez le reCAPTCHA, puis répétez l'étape précédente pour que l'utilisateur puisse réessayer :

    API Web modulaire

    recaptchaVerifier.clear();
    

    API d'espace de noms Web

    recaptchaVerifier.clear();
    
  7. Une fois le code SMS envoyé, demandez à l'utilisateur de vérifier le code :

    API Web modulaire

    const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
    

    API d'espace de noms Web

    // Ask user for the verification code. Then:
    var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
    
  8. Initialisez un objet MultiFactorAssertion avec PhoneAuthCredential :

    API Web modulaire

    const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
    

    API d'espace de noms Web

    var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
    
  9. Appelez resolver.resolveSignIn() pour terminer l'authentification secondaire. Vous pouvez ensuite accéder au résultat de connexion d'origine, qui inclut les données standard spécifiques au fournisseur et les identifiants d'authentification :

    API Web modulaire

    // Complete sign-in. This will also trigger the Auth state listeners.
    resolver.resolveSignIn(multiFactorAssertion)
        .then(function (userCredential) {
            // userCredential will also contain the user, additionalUserInfo, optional
            // credential (null for email/password) associated with the first factor sign-in.
    
            // For example, if the user signed in with Google as a first factor,
            // userCredential.additionalUserInfo will contain data related to Google 
            // provider that the user signed in with.
            // - user.credential contains the Google OAuth credential.
            // - user.credential.accessToken contains the Google OAuth access token.
            // - user.credential.idToken contains the Google OAuth ID token.
        });
    

    API d'espace de noms Web

    // Complete sign-in. This will also trigger the Auth state listeners.
    resolver.resolveSignIn(multiFactorAssertion)
      .then(function(userCredential) {
        // userCredential will also contain the user, additionalUserInfo, optional
        // credential (null for email/password) associated with the first factor sign-in.
        // For example, if the user signed in with Google as a first factor,
        // userCredential.additionalUserInfo will contain data related to Google provider that
        // the user signed in with.
        // user.credential contains the Google OAuth credential.
        // user.credential.accessToken contains the Google OAuth access token.
        // user.credential.idToken contains the Google OAuth ID token.
      });
    

Le code ci-dessous montre un exemple complet de connexion d'un utilisateur multifacteur :

API Web modulaire

import {
    getAuth,
    getMultiFactorResolver,
    PhoneAuthProvider,
    PhoneMultiFactorGenerator,
    RecaptchaVerifier,
    signInWithEmailAndPassword
} from "firebase/auth";

const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);

const auth = getAuth();
signInWithEmailAndPassword(auth, email, password)
    .then(function (userCredential) {
        // User is not enrolled with a second factor and is successfully
        // signed in.
        // ...
    })
    .catch(function (error) {
        if (error.code == 'auth/multi-factor-auth-required') {
            const resolver = getMultiFactorResolver(auth, error);
            // Ask user which second factor to use.
            if (resolver.hints[selectedIndex].factorId ===
                PhoneMultiFactorGenerator.FACTOR_ID) {
                const phoneInfoOptions = {
                    multiFactorHint: resolver.hints[selectedIndex],
                    session: resolver.session
                };
                const phoneAuthProvider = new PhoneAuthProvider(auth);
                // Send SMS verification code
                return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
                    .then(function (verificationId) {
                        // Ask user for the SMS verification code. Then:
                        const cred = PhoneAuthProvider.credential(
                            verificationId, verificationCode);
                        const multiFactorAssertion =
                            PhoneMultiFactorGenerator.assertion(cred);
                        // Complete sign-in.
                        return resolver.resolveSignIn(multiFactorAssertion)
                    })
                    .then(function (userCredential) {
                        // User successfully signed in with the second factor phone number.
                    });
            } else {
                // Unsupported second factor.
            }
        } else if (error.code == 'auth/wrong-password') {
            // Handle other errors such as wrong password.
        }
    });

API d'espace de noms Web

var resolver;
firebase.auth().signInWithEmailAndPassword(email, password)
  .then(function(userCredential) {
    // User is not enrolled with a second factor and is successfully signed in.
    // ...
  })
  .catch(function(error) {
    if (error.code == 'auth/multi-factor-auth-required') {
      resolver = error.resolver;
      // Ask user which second factor to use.
      if (resolver.hints[selectedIndex].factorId ===
          firebase.auth.PhoneMultiFactorGenerator.FACTOR_ID) {
        var phoneInfoOptions = {
          multiFactorHint: resolver.hints[selectedIndex],
          session: resolver.session
        };
        var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
        // Send SMS verification code
        return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
          .then(function(verificationId) {
            // Ask user for the SMS verification code.
            var cred = firebase.auth.PhoneAuthProvider.credential(
                verificationId, verificationCode);
            var multiFactorAssertion =
                firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
            // Complete sign-in.
            return resolver.resolveSignIn(multiFactorAssertion)
          })
          .then(function(userCredential) {
            // User successfully signed in with the second factor phone number.
          });
      } else {
        // Unsupported second factor.
      }
    } else if (error.code == 'auth/wrong-password') {
      // Handle other errors such as wrong password.
    } ...
  });

Toutes nos félicitations! Vous avez réussi à connecter un utilisateur à l'aide de l'authentification multifacteur.

Et après