Aggiungere l'autenticazione a più fattori all'app Flutter

Se hai eseguito l'upgrade a Firebase Authentication con Identity Platform, puoi aggiungere l'autenticazione a più fattori tramite SMS alla tua app Flutter.

L'autenticazione a più fattori (MFA) aumenta la sicurezza della tua app. Sebbene gli utenti malintenzionati spesso compromettano password e account social, intercettare un messaggio di testo è più difficile.

Prima di iniziare

  1. Attiva almeno un provider che supporta l'autenticazione a più fattori. Tutti i provider supportano MFA, tranne l'autenticazione telefonica, l'autenticazione anonima e l'Apple Game Center.

  2. Assicurati che la tua app verifichi le email degli utenti. La verifica in due passaggi richiede la verifica email. In questo modo, i malintenzionati non possono registrarsi a un servizio con un'email di cui non sono proprietari e quindi bloccare il vero proprietario aggiungendo un secondo fattore.

  3. Android: se non hai ancora impostato l'hash SHA-256 della tua app nella console Firebase, fallo. Consulta la sezione Autenticazione del client per informazioni su come trovare l'hash SHA-256 della tua app.

  4. iOS: in Xcode, abilita le notifiche push per il tuo progetto e assicurati che la chiave di autenticazione APN sia configurata con Firebase Cloud Messaging (FCM). Inoltre, devi attivare le modalità in background per le notifiche remote. Per una spiegazione dettagliata di questo passaggio, consulta la documentazione di Firebase Phone Auth per iOS.

  5. Web: assicurati di aver aggiunto il dominio delle tue applicazioni nella sezione Domini di reindirizzamento OAuth della console di Firebase.

Attivazione dell'autenticazione a più fattori

  1. Apri la pagina Autenticazione > Metodo di accesso della Console Firebase.

  2. Nella sezione Avanzate, attiva Autenticazione a più fattori tramite SMS.

    Dovresti anche inserire i numeri di telefono con cui testerai l'app. Sebbene sia facoltativo, ti consigliamo vivamente di registrare i numeri di telefono per i test al fine di evitare limitazioni durante lo sviluppo.

  3. Se non hai ancora autorizzato il dominio della tua app, aggiungilo alla lista consentita nella pagina Autenticazione > Impostazioni della console Firebase.

Scegliere un modello di registrazione

Puoi scegliere se la tua app richiede l'autenticazione a più fattori, nonché come e quando registrare gli utenti. Alcuni pattern comuni sono:

  • Registra il secondo fattore dell'utente durante la registrazione. Utilizza questo metodo se la tua app richiede l'autenticazione a più fattori per tutti gli utenti.

  • Offri un'opzione ignorabile per registrare un secondo fattore durante la registrazione. Le app che vogliono incoraggiare, ma non richiedono, l'autenticazione a più fattori potrebbero preferire questo approccio.

  • Consente di aggiungere un secondo fattore dalla pagina di gestione dell'account o del profilo dell'utente, anziché dalla schermata di registrazione. In questo modo si riduce al minimo l'attrito durante il processo di registrazione, rendendo comunque disponibile l'autenticazione a più fattori per gli utenti sensibili alla sicurezza.

  • Richiedi l'aggiunta di un secondo fattore in modo incrementale quando l'utente vuole accedere alle funzionalità con requisiti di sicurezza più elevati.

Registrazione di un secondo fattore

Per registrare un nuovo fattore secondario per un utente:

  1. Esegui nuovamente l'autenticazione dell'utente.

  2. Chiedi all'utente di inserire il proprio numero di telefono.

  3. Ottieni una sessione con autenticazione a più fattori per l'utente:

    final multiFactorSession = await user.multiFactor.getSession();
    
  4. Verifica il numero di telefono con una sessione multifattoriale e i tuoi rilanci:

    await FirebaseAuth.instance.verifyPhoneNumber(
      multiFactorSession: multiFactorSession,
      phoneNumber: phoneNumber,
      verificationCompleted: (_) {},
      verificationFailed: (_) {},
      codeSent: (String verificationId, int? resendToken) async {
        // The SMS verification code has been sent to the provided phone number.
        // ...
      },
      codeAutoRetrievalTimeout: (_) {},
    ); 
    
  5. Una volta inviato il codice SMS, chiedi all'utente di verificarlo:

    final credential = PhoneAuthProvider.credential(
      verificationId: verificationId,
      smsCode: smsCode,
    );
    
  6. Completa la registrazione:

    await user.multiFactor.enroll(
      PhoneMultiFactorGenerator.getAssertion(
        credential,
      ),
    );
    

Il codice seguente mostra un esempio completo di registrazione di un secondo fattore:

  final session = await user.multiFactor.getSession();
  final auth = FirebaseAuth.instance;
  await auth.verifyPhoneNumber(
    multiFactorSession: session,
    phoneNumber: phoneController.text,
    verificationCompleted: (_) {},
    verificationFailed: (_) {},
    codeSent: (String verificationId, int? resendToken) async {
      // See `firebase_auth` example app for a method of retrieving user's sms code: 
      // https://github.com/firebase/flutterfire/blob/master/packages/firebase_auth/firebase_auth/example/lib/auth.dart#L591
      final smsCode = await getSmsCodeFromUser(context);

      if (smsCode != null) {
        // Create a PhoneAuthCredential with the code
        final credential = PhoneAuthProvider.credential(
          verificationId: verificationId,
          smsCode: smsCode,
        );

        try {
          await user.multiFactor.enroll(
            PhoneMultiFactorGenerator.getAssertion(
              credential,
            ),
          );
        } on FirebaseAuthException catch (e) {
          print(e.message);
        }
      }
    },
    codeAutoRetrievalTimeout: (_) {},
  );

Complimenti! Hai registrato un secondo fattore di autenticazione per un utente.

Accedere con un secondo fattore

Per consentire a un utente di accedere con la verifica SMS a due fattori:

  1. Accedi all'utente con il primo fattore, quindi rileva l'eccezione FirebaseAuthMultiFactorException. Questo errore contiene un resolver che puoi utilizzare per ottenere i secondi fattori registrati dell'utente. Contiene anche una sessione sottostante che dimostra che l'utente si è autenticato correttamente con il primo fattore.

    Ad esempio, se il primo fattore dell'utente era un indirizzo email e una password:

    try {
      await _auth.signInWithEmailAndPassword(
          email: emailController.text,
          password: passwordController.text,
      );
      // User is not enrolled with a second factor and is successfully
      // signed in.
      // ...
    } on FirebaseAuthMultiFactorException catch (e) {
      // The user is a multi-factor user. Second factor challenge is required
      final resolver = e.resolver
      // ...
    }
    
  2. Se l'utente ha registrato più fattori secondari, chiedigli quale usare:

    final session = e.resolver.session;
    
    final hint = e.resolver.hints[selectedHint];
    
  3. Invia un messaggio di verifica al telefono dell'utente con il suggerimento e la sessione a più fattori:

    await FirebaseAuth.instance.verifyPhoneNumber(
      multiFactorSession: session,
      multiFactorInfo: hint,
      verificationCompleted: (_) {},
      verificationFailed: (_) {},
      codeSent: (String verificationId, int? resendToken) async {
        // ...
      },
      codeAutoRetrievalTimeout: (_) {},
    );
    
  4. Chiama resolver.resolveSignIn() per completare l'autenticazione secondaria:

    final smsCode = await getSmsCodeFromUser(context);
    if (smsCode != null) {
      // Create a PhoneAuthCredential with the code
      final credential = PhoneAuthProvider.credential(
        verificationId: verificationId,
        smsCode: smsCode,
      );
    
      try {
        await e.resolver.resolveSignIn(
          PhoneMultiFactorGenerator.getAssertion(credential)
        );
      } on FirebaseAuthException catch (e) {
        print(e.message);
      }
    }
    

Il codice seguente mostra un esempio completo di accesso di un utente con autenticazione a più fattori:

try {
  await _auth.signInWithEmailAndPassword(
    email: emailController.text,
    password: passwordController.text,
  );
} on FirebaseAuthMultiFactorException catch (e) {
  setState(() {
    error = '${e.message}';
  });
  final firstHint = e.resolver.hints.first;
  if (firstHint is! PhoneMultiFactorInfo) {
    return;
  }
  await FirebaseAuth.instance.verifyPhoneNumber(
    multiFactorSession: e.resolver.session,
    multiFactorInfo: firstHint,
    verificationCompleted: (_) {},
    verificationFailed: (_) {},
    codeSent: (String verificationId, int? resendToken) async {
      // See `firebase_auth` example app for a method of retrieving user's sms code: 
      // https://github.com/firebase/flutterfire/blob/master/packages/firebase_auth/firebase_auth/example/lib/auth.dart#L591
      final smsCode = await getSmsCodeFromUser(context);

      if (smsCode != null) {
        // Create a PhoneAuthCredential with the code
        final credential = PhoneAuthProvider.credential(
          verificationId: verificationId,
          smsCode: smsCode,
        );

        try {
          await e.resolver.resolveSignIn(
            PhoneMultiFactorGenerator.getAssertion(
              credential,
            ),
          );
        } on FirebaseAuthException catch (e) {
          print(e.message);
        }
      }
    },
    codeAutoRetrievalTimeout: (_) {},
  );
} catch (e) {
  ...
} 

Complimenti! Hai eseguito l'accesso a un utente utilizzando l'autenticazione a più fattori.

Passaggi successivi