Ajouter l'authentification multifacteur à votre application iOS

Ce document explique comment ajouter l'authentification multifacteur par SMS à votre application iOS.

L'authentification multifacteur renforce la sécurité de votre application. Bien que les pirates informatiques compromettent les mots de passe et les comptes de réseaux sociaux, l'interception des SMS est plus difficile.

Avant de commencer

  1. Activez au moins un fournisseur compatible avec l'authentification multifacteur. Tous les fournisseurs sont compatibles avec la MFA, sauf l'authentification par téléphone, l'authentification anonyme et l'Apple Game Center.

  2. Assurez-vous que votre application valide les adresses e-mail des utilisateurs. L'authentification multifacteur nécessite une validation de l'adresse e-mail. Cela empêche les utilisateurs malveillants de s'enregistrer à un service avec une adresse e-mail dont ils ne sont pas propriétaire, puis de bloquer le propriétaire réel en ajoutant un second facteur.

Activer l'authentification multifacteur

  1. Accédez à la page Identity Platform MFA dans la console Google Cloud.
    Accéder à la page MFA

  2. Dans la zone intitulée Authentification multifacteur par SMS, cliquez sur Activer.

  3. Saisissez les numéros de téléphone avec lesquels vous souhaitez tester votre application. Bien que cette étape soit facultative, il est fortement recommandé d'enregistrer des numéros de téléphone de test pour éviter toute limitation lors du développement.

  4. Si vous n'avez pas encore autorisé le domaine de votre application, ajoutez-le à la liste des autorisations en cliquant sur Ajouter un domaine à droite.

  5. Cliquez sur Enregistrer.

Valider votre application

Identity Platform doit vérifier que les requêtes SMS proviennent de votre application. Pour cela, vous avez le choix entre deux méthodes :

  • Notifications APN silencieuses : lorsque vous connectez pour la première fois un utilisateur, Identity Platform peut envoyer une notification push silencieuse à l'appareil de l'utilisateur. L'authentification peut se poursuivre 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.

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

Utiliser les notifications silencieuses

Pour activer les notifications APN pour une utilisation avec Identity Platform, procédez comme suit :

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

  2. Importez votre clé d'authentification d'APN à l'aide de la console Firebase (vos modifications seront automatiquement transférées vers Google Cloud Identity Platform). Si vous ne possédez pas déjà votre clé d'authentification d'APN, consultez la page Configurer les APN avec FCM pour savoir comment l'obtenir.

    1. Ouvrez la console Firebase.

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

    3. Sélectionnez l'onglet Cloud Messaging.

    4. Sous Clés d'authentification d'APN, dans la section Configuration de l'application iOS, cliquez sur Importer.

    5. Sélectionnez votre clé.

    6. Ajoutez l'ID de la clé. Vous pouvez trouver l'ID de clé sous Certificats, identifiants et profils dans Apple Developer Member Center.

    7. Cliquez sur Importer.

Si vous disposez déjà d'un certificat APN, vous pouvez l'importer à la place.

Utiliser la validation reCAPTCHA

Pour permettre au SDK client d'utiliser reCAPTCHA, procédez comme suit:

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

  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. Cette valeur est répertoriée dans le fichier de configuration GoogleService-Info.plist sous la forme REVERSED_CLIENT_ID.

Une fois terminée, votre configuration ressemble normalement à ce qui suit :

Schémas personnalisés

Vous pouvez éventuellement personnaliser la manière dont votre application présente SFSafariViewController ou UIWebView lors de l'affichage de 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 décider si votre application nécessite une authentification multifacteur, et comment et quand inscrire vos utilisateurs. Voici quelques modèles courants :

  • 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 disposer d'une adresse e-mail validée pour activer un deuxième facteur. Votre parcours d'inscription doit donc le prendre en compte.

  • Proposez une option désactivable pour inscrire un second facteur lors de l'enregistrement. Les applications qui souhaitent encourager mais pas exiger l'authentification multifacteur peuvent préférer cette approche.

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

  • Exigez l'ajout incrémentiel d'un second facteur lorsque l'utilisateur souhaite accéder aux fonctionnalités présentant des exigences de sécurité accrues.

Inscrire un second facteur

Pour inscrire un nouveau facteur secondaire pour un utilisateur, procédez comme suit :

  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 :

    Swift

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

    Objective-C

    [authResult.user.multiFactor
      getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                NSError * _Nullable error) {
        // ...
    }];
    
  4. Envoyez un message de validation sur le téléphone de l'utilisateur. Assurez-vous que le numéro de téléphone commence par +, sans aucun autre signe de ponctuation ni espace blanc (par exemple, +15105551234).

    Swift

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

    Objective-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 préalablement les utilisateurs qu'ils recevront un SMS, et que les tarifs standards s'appliquent.

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

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

    Swift

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

    Objective-C

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

    Swift

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  7. Terminez l'inscription. Vous pouvez éventuellement spécifier un nom à afficher pour le deuxième facteur. Cela s'avère utile pour les utilisateurs qui ont plusieurs facteurs, car le numéro de téléphone est masqué pendant le processus d'authentification (par exemple, +1******1234).

    Swift

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

    Objective-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 second facteur :

Swift

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

Objective-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) {
            // ...
        }];
    }];
}];

Félicitations ! Vous avez enregistré un second facteur d'authentification pour un utilisateur.

Connecter des utilisateurs avec un second facteur

Pour connecter un utilisateur via la validation SMS à deux facteurs, procédez comme suit :

  1. Connectez l'utilisateur avec son premier facteur, puis détectez une erreur indiquant que l'authentification multifacteur est requise. Cette erreur contient un résolveur, des indices sur les seconds facteurs inscrits et une session sous-jacente attestant que l'utilisateur s'est authentifié avec succès avec le premier facteur.

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

    Swift

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

    Objective-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 qu'OAuth, détectez l'erreur après avoir appelé getCredentialWith().

  2. Si l'utilisateur a enregistré 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 à afficher avec resolver.hints[selectedIndex].displayName.

    Swift

    // 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.
    }
    

    Objective-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 validation sur le téléphone de l'utilisateur :

    Swift

    // 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.
    }
    

    Objective-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 le valider et de l'utiliser pour créer un PhoneAuthCredential:

    Swift

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

    Objective-C

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

    Swift

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion =
        [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  6. Résolvez la connexion. Vous pouvez ensuite accéder au résultat de connexion d'origine, qui inclut les données spécifiques au fournisseur et les identifiants d'authentification :

    Swift

    // 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.
    }
    

    Objective-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 illustre un exemple complet de connexion d'un utilisateur multifacteur :

Swift

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

Objective-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.
                }
            }];
        }];
    }
}];

Félicitations ! Vous avez connecté un utilisateur à l'aide de l'authentification multifacteur.

Étapes suivantes