Ajoutez l'authentification multifacteur à votre application iOS

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

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.

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.

Vérifier votre application

Firebase doit vérifier que les demandes SMS proviennent de votre application. Vous pouvez le faire de deux manières :

  • Notifications APN silencieuses : lorsque vous connectez un utilisateur pour la première fois, Firebase peut envoyer une notification push silencieuse à l'appareil de l'utilisateur. L'authentification peut avoir lieu si l'application reçoit la notification. Notez qu'à partir d'iOS 8.0, vous n'avez pas besoin de demander à l'utilisateur d'autoriser les notifications push pour utiliser cette méthode.

  • Vérification reCAPTCHA : si vous ne pouvez pas envoyer de notification silencieuse (par exemple, parce que l'utilisateur a désactivé l'actualisation en arrière-plan ou que vous testez votre application dans le simulateur iOS), vous pouvez utiliser reCAPTCHA. Dans de nombreux cas, le reCAPTCHA se résoudra automatiquement sans aucune interaction de l'utilisateur.

Utiliser les notifications silencieuses

Pour activer les notifications APN à utiliser avec Firebase :

  1. Dans Xcode, activez les notifications push pour votre projet.

  2. Téléchargez votre clé d'authentification APNs à l'aide de la console Firebase (vos modifications seront automatiquement transférées vers Google Cloud Firebase). Si vous ne disposez pas déjà de votre clé d'authentification APN, consultez Configuration des APN avec FCM pour savoir comment l'obtenir.

    1. Ouvrez la console Firebase .

    2. Accédez à Paramètres du projet .

    3. Sélectionnez l'onglet Messagerie cloud .

    4. Sous Clé d'authentification APNs , dans la section Configuration de l'application iOS , cliquez sur Télécharger .

    5. Sélectionnez votre clé.

    6. Ajoutez l’ID de clé pour la clé. Vous pouvez trouver l'ID de clé sous Certificats, identifiants et profils dans le Centre des membres des développeurs Apple .

    7. Cliquez sur Télécharger .

Si vous disposez déjà d'un certificat APNs, vous pouvez télécharger le certificat à la place.

Utiliser la vérification reCAPTCHA

Pour permettre au SDK client d'utiliser reCAPTCHA :

  1. Ouvrez la configuration de votre projet dans Xcode.

  2. Double-cliquez sur le nom du projet dans l'arborescence de gauche.

  3. Sélectionnez votre application dans la section Cibles .

  4. Sélectionnez l'onglet Informations .

  5. Développez la section Types d'URL .

  6. Cliquez sur le bouton + .

  7. Saisissez votre ID client inversé dans le champ Schémas d'URL . Vous pouvez trouver cette valeur répertoriée dans le fichier de configuration GoogleService-Info.plist sous le nom REVERSED_CLIENT_ID .

Une fois terminée, votre configuration devrait ressembler à ce qui suit :

Schémas personnalisés

En option, vous pouvez personnaliser la façon dont votre application présente le SFSafariViewController ou UIWebView lors de l'affichage du reCAPTCHA. Pour ce faire, créez une classe personnalisée conforme au protocole FIRAuthUIDelegate et transmettez-la à verifyPhoneNumber:UIDelegate:completion: .

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. Notez qu'un compte doit avoir une adresse e-mail vérifiée pour inscrire un deuxième facteur, votre flux d'inscription devra donc en tenir compte.

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

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. Obtenez une session multifacteur pour l'utilisateur :

    Rapide

    authResult.user.multiFactor.getSessionWithCompletion() { (session, error) in
      // ...
    }
    

    Objectif c

    [authResult.user.multiFactor
      getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                NSError * _Nullable error) {
        // ...
    }];
    
  4. Envoyez un message de vérification au téléphone de l'utilisateur. Assurez-vous que le numéro de téléphone est formaté avec un signe + et aucun autre signe de ponctuation ou espace (par exemple : +15105551234 ).

    Rapide

    // Send SMS verification code.
    PhoneAuthProvider.provider().verifyPhoneNumber(
      phoneNumber,
      uiDelegate: nil,
      multiFactorSession: session) { (verificationId, error) in
        // verificationId will be needed for enrollment completion.
    }
    

    Objectif c

    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider verifyPhoneNumber:phoneNumber
                                          UIDelegate:nil
                                  multiFactorSession:session
                                          completion:^(NSString * _Nullable verificationID,
                                                        NSError * _Nullable error) {
        // 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.

    La méthode verifyPhoneNumber() démarre le processus de vérification de l'application en arrière-plan à l'aide d'une notification push silencieuse. Si la notification push silencieuse n'est pas disponible, un défi reCAPTCHA est émis à la place.

  5. Une fois le code SMS envoyé, demandez à l'utilisateur de vérifier le code. Ensuite, utilisez leur réponse pour créer un PhoneAuthCredential :

    Rapide

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId,
      verificationCode: verificationCode)
    

    Objectif c

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential = [FIRPhoneAuthProvider.provider
                                           credentialWithVerificationID:verificationID
                                           verificationCode:kPhoneSecondFactorVerificationCode];
    
  6. Initialisez un objet d'assertion :

    Rapide

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objectif c

    FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  7. 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).

    Rapide

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
    

    Objectif c

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    [authResult.user.multiFactor enrollWithAssertion:assertion
                                         displayName:nil
                                          completion:^(NSError * _Nullable error) {
        // ...
    }];
    

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

Rapide

let user = Auth.auth().currentUser
user?.multiFactor.getSessionWithCompletion({ (session, error) in
  // Send SMS verification code.
  PhoneAuthProvider.provider().verifyPhoneNumber(
    phoneNumber,
    uiDelegate: nil,
    multiFactorSession: session
  ) { (verificationId, error) in
    // verificationId will be needed for enrollment completion.
    // Ask user for the verification code.
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: phoneSecondFactorVerificationCode)
    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user?.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
  }
})

Objectif c

FIRUser *user = FIRAuth.auth.currentUser;
[user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                              NSError * _Nullable error) {
    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumber:phoneNumber
      UIDelegate:nil
      multiFactorSession:session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        // verificationId will be needed for enrollment completion.

        // Ask user for the verification code.
        // ...

        // Then:
        FIRPhoneAuthCredential *credential =
            [FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID
                                                        verificationCode:kPhoneSecondFactorVerificationCode];
        FIRMultiFactorAssertion *assertion =
            [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

        // Complete enrollment. This will update the underlying tokens
        // and trigger ID token change listener.
        [user.multiFactor enrollWithAssertion:assertion
                                  displayName:displayName
                                    completion:^(NSError * _Nullable error) {
            // ...
        }];
    }];
}];

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 une erreur indiquant qu'une authentification multifacteur est requise. 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 :

    Rapide

    Auth.auth().signIn(
      withEmail: email,
      password: password
    ) { (result, error) in
      let authError = error as NSError
      if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
        // The user is a multi-factor user. Second factor challenge is required.
        let resolver =
          authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
        // ...
      } else {
        // Handle other errors such as wrong password.
      }
    }
    

    Objectif c

    [FIRAuth.auth signInWithEmail:email
                         password:password
                       completion:^(FIRAuthDataResult * _Nullable authResult,
                                    NSError * _Nullable error) {
        if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
            // User is not enrolled with a second factor and is successfully signed in.
            // ...
        } else {
            // The user is a multi-factor user. Second factor challenge is required.
        }
    }];
    

    Si le premier facteur de l'utilisateur est un fournisseur fédéré, tel que OAuth, détectez l'erreur après avoir appelé getCredentialWith() .

  2. Si l’utilisateur a inscrit plusieurs facteurs secondaires, demandez-lui lequel utiliser. Vous pouvez obtenir le numéro de téléphone masqué avec resolver.hints[selectedIndex].phoneNumber et le nom d'affichage avec resolver.hints[selectedIndex].displayName .

    Rapide

    // Ask user which second factor to use. Then:
    if resolver.hints[selectedIndex].factorID == PhoneMultiFactorID {
      // User selected a phone second factor.
      // ...
    } else if resolver.hints[selectedIndex].factorID == TotpMultiFactorID {
      // User selected a TOTP second factor.
      // ...
    } else {
      // Unsupported second factor.
    }
    

    Objectif c

    FIRMultiFactorResolver *resolver =
        (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
    
    // Ask user which second factor to use. Then:
    FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];
    if (hint.factorID == FIRPhoneMultiFactorID) {
      // User selected a phone second factor.
      // ...
    } else if (hint.factorID == FIRTOTPMultiFactorID) {
      // User selected a TOTP second factor.
      // ...
    } else {
      // Unsupported second factor.
    }
    
  3. Envoyez un message de vérification au téléphone de l'utilisateur :

    Rapide

    // Send SMS verification code.
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      // verificationId will be needed for sign-in completion.
    }
    

    Objectif c

    // Send SMS verification code
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumberWithMultiFactorInfo:hint
      UIDelegate:nil
      multiFactorSession:resolver.session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        if (error != nil) {
            // Failed to verify phone number.
        }
    }];
    
  4. Une fois le code SMS envoyé, demandez à l'utilisateur de vérifier le code et de l'utiliser pour construire un PhoneAuthCredential :

    Rapide

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: verificationCodeFromUser)
    

    Objectif c

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential =
        [FIRPhoneAuthProvider.provider
          credentialWithVerificationID:verificationID
                      verificationCode:verificationCodeFromUser];
    
  5. Initialisez un objet d'assertion avec les informations d'identification :

    Rapide

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objectif c

    FIRMultiFactorAssertion *assertion =
        [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  6. Résolvez la connexion. 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 :

    Rapide

    // Complete sign-in. This will also trigger the Auth state listeners.
    resolver.resolveSignIn(with: assertion) { (authResult, error) in
      // authResult 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,
      // authResult.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.
    }
    

    Objectif c

    // Complete sign-in.
    [resolver resolveSignInWithAssertion:assertion
                              completion:^(FIRAuthDataResult * _Nullable authResult,
                                            NSError * _Nullable error) {
        if (error != nil) {
            // User successfully signed in with the second factor phone number.
        }
    }];
    

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

Rapide

Auth.auth().signIn(
  withEmail: email,
  password: password
) { (result, error) in
  let authError = error as NSError?
  if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
    let resolver =
      authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver

    // Ask user which second factor to use.
    // ...

    // Then:
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo

    // Send SMS verification code
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      if error != nil {
        // Failed to verify phone number.
      }
      // Ask user for the SMS verification code.
      // ...

      // Then:
      let credential = PhoneAuthProvider.provider().credential(
        withVerificationID: verificationId!,
        verificationCode: verificationCodeFromUser)
      let assertion = PhoneMultiFactorGenerator.assertion(with: credential)

      // Complete sign-in.
      resolver.resolveSignIn(with: assertion) { (authResult, error) in
        if error != nil {
          // User successfully signed in with the second factor phone number.
        }
      }
    }
  }
}

Objectif c

[FIRAuth.auth signInWithEmail:email
                     password:password
                   completion:^(FIRAuthDataResult * _Nullable authResult,
                               NSError * _Nullable error) {
    if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
        // User is not enrolled with a second factor and is successfully signed in.
        // ...
    } else {
        FIRMultiFactorResolver *resolver =
            (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];

        // Ask user which second factor to use.
        // ...

        // Then:
        FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];

        // Send SMS verification code
        [FIRPhoneAuthProvider.provider
          verifyPhoneNumberWithMultiFactorInfo:hint
                                    UIDelegate:nil
                            multiFactorSession:resolver.session
                                    completion:^(NSString * _Nullable verificationID,
                                                NSError * _Nullable error) {
            if (error != nil) {
                // Failed to verify phone number.
            }

            // Ask user for the SMS verification code.
            // ...

            // Then:
            FIRPhoneAuthCredential *credential =
                [FIRPhoneAuthProvider.provider
                  credentialWithVerificationID:verificationID
                              verificationCode:kPhoneSecondFactorVerificationCode];
            FIRMultiFactorAssertion *assertion =
                [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

            // Complete sign-in.
            [resolver resolveSignInWithAssertion:assertion
                                      completion:^(FIRAuthDataResult * _Nullable authResult,
                                                    NSError * _Nullable error) {
                if (error != nil) {
                    // User successfully signed in with the second factor phone number.
                }
            }];
        }];
    }
}];

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

Et après