Ajoutez l'authentification multifacteur à votre application Web

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

L'authentification multifacteur augmente la sécurité de votre application. Même si les attaquants compromettent souvent les mots de passe et les comptes sociaux, il est plus difficile d’intercepter un message texte.

Avant que tu commences

  1. Activez au moins un fournisseur prenant en charge l’authentification multifacteur. Tous les fournisseurs prennent en charge MFA, à l'exception de l'authentification téléphonique, 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 une adresse e-mail qui ne leur appartient pas, puis de verrouiller le véritable propriétaire en ajoutant un deuxième facteur.

Utilisation de la multilocation

Si vous activez l'authentification multifacteur pour une utilisation dans un environnement multi-tenant , 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 modulaire Web

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

    API avec espace de noms Web

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

Activation de l'authentification multifacteur

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

  2. Dans la section Avancé , activez l'authentification multifacteur SMS .

    Vous devez également saisir 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 toute limitation 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 nécessite une authentification multifacteur, ainsi que 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'enregistrement, tout en rendant l'authentification multifacteur disponible pour les utilisateurs sensibles à la sécurité.

  • Exiger l'ajout d'un deuxième facteur de manière incrémentielle lorsque l'utilisateur souhaite accéder à des fonctionnalités présentant des exigences de sécurité accrues.

Configuration du vérificateur reCAPTCHA

Avant de pouvoir envoyer des codes SMS, vous devez configurer un vérificateur reCAPTCHA. Firebase utilise reCAPTCHA pour éviter les abus en garantissant 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 toutes les clés et secrets client nécessaires.

Utiliser 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 modulaire Web

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 avec 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 modulaire Web

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 avec 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

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

API modulaire Web

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

API avec 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 élimine cette logique avec la méthode verify , vous n'avez donc pas besoin de gérer directement la variable grecaptcha .

Inscription d'un deuxième facteur

Pour inscrire un nouveau facteur secondaire pour un utilisateur :

  1. Réauthentifiez l'utilisateur.

  2. Demandez à l'utilisateur de saisir 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 modulaire Web

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

    API avec espace de noms Web

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

    API modulaire Web

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

    API avec 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 modulaire Web

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

    API avec 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 modulaire Web

    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 avec 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 message SMS et que les tarifs standard s'appliqueront.

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

    recaptchaVerifier.clear();
    

    API avec espace de noms Web

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

    API modulaire Web

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

    API avec 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 le PhoneAuthCredential :

    API modulaire Web

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

    API avec espace de noms Web

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

    API modulaire Web

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

    API avec 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 modulaire Web

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 avec 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.

Connecter les utilisateurs avec un deuxième facteur

Pour connecter un utilisateur avec une vérification 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 indications 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 un e-mail et un mot de passe :

    API modulaire Web

    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 avec 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, détectez l'erreur après avoir appelé signInWithPopup() ou signInWithRedirect() .

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

    API modulaire 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 ===
        PhoneMultiFactorGenerator.FACTOR_ID) {
        // User selected a phone second factor.
        // ...
    } else if (resolver.hints[selectedIndex].factorId ===
               TotpMultiFactorGenerator.FACTOR_ID) {
        // User selected a TOTP second factor.
        // ...
    } else {
        // Unsupported second factor.
    }
    

    API avec 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 if (resolver.hints[selectedIndex].factorId === firebase.auth.TotpMultiFactorGenerator.FACTOR_ID) {
      // User selected a TOTP second factor.
      // ...
    } else {
      // Unsupported second factor.
    }
    
  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 modulaire Web

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

    API avec 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 modulaire Web

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

    API avec 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 modulaire Web

    // 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 avec 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 modulaire Web

    recaptchaVerifier.clear();
    

    API avec espace de noms Web

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

    API modulaire Web

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

    API avec 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 le PhoneAuthCredential :

    API modulaire Web

    const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
    

    API avec 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 comprend les données standard spécifiques au fournisseur et les informations d'authentification :

    API modulaire 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.
        });
    

    API avec 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 modulaire Web

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 if (resolver.hints[selectedIndex].factorId ===
                       TotpMultiFactorGenerator.FACTOR_ID) {
                // Handle TOTP MFA.
                // ...
            } else {
                // Unsupported second factor.
            }
        } else if (error.code == 'auth/wrong-password') {
            // Handle other errors such as wrong password.
        }
    });

API avec 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 if (resolver.hints[selectedIndex].factorId ===
        firebase.auth.TotpMultiFactorGenerator.FACTOR_ID) {
        // Handle TOTP MFA.
        // ...
      } 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