Dodawanie uwierzytelniania wielopoziomowego do aplikacji Flutter

Jeśli masz uaktualnione Uwierzytelnianie Firebase z Identity Platform, możesz dodać do aplikacji na Fluttera uwierzytelnianie wielopoziomowe z użyciem SMS-ów.

Uwierzytelnianie wielopoziomowe (MFA) zwiększa bezpieczeństwo aplikacji. Osoby przeprowadzające ataki często przejmują hasła i konta społecznościowe, ale przechwycenie SMS-a jest trudniejsze.

Zanim zaczniesz

nie jest obsługiwane w Flutterze.
  1. Włącz co najmniej jednego dostawcę obsługującego uwierzytelnianie wielopoziomowe. Każdy dostawca obsługuje uwierzytelnianie wieloskładnikowe z wyjątkiem uwierzytelniania przez telefon, anonimowego i za pomocą Apple Game Center.

  2. Sprawdź, czy aplikacja weryfikuje adresy e-mail użytkowników. MFA wymaga weryfikacji adresu e-mail. Zapobiega to rejestrowaniu się hakerów w usłudze za pomocą adresu e-mail, który nie należy do nich, a następnie blokowaniu dostępu do konta prawdziwemu właścicielowi przez dodanie drugiego etapu weryfikacji.

  3. Android: jeśli nie masz jeszcze ustawionego identyfikatora SHA-256 aplikacji w konsoli Firebase, zrób to. Więcej informacji o znajdowaniu identyfikatora SHA-256 aplikacji znajdziesz w artykule Uwierzytelnianie klienta.

  4. iOS: w Xcode włącz powiadomienia push w projekcie i upewnij się, że klucz uwierzytelniania APNs jest skonfigurowany z Komunikacją w chmurze Firebase (FCM). Musisz też włączyć tryby działania w tle w przypadku powiadomień zdalnych. Szczegółowe wyjaśnienie tego kroku znajdziesz w dokumentacji Firebase iOS Phone Auth.

  5. Internet: upewnij się, że domena aplikacji została dodana w konsoli Firebase w sekcji Domeny przekierowania OAuth.

Włączanie uwierzytelniania wielopoziomowego

  1. Otwórz stronę Uwierzytelnianie > Metoda logowania w konsoli Firebase.

  2. W sekcji Zaawansowane włącz Uwierzytelnianie wielopoziomowe z użyciem SMS-ów.

    Podaj też numery telefonów, których użyjesz do testowania aplikacji. Chociaż rejestracja numerów telefonów do testów jest opcjonalna, zdecydowanie ją zalecamy, ponieważ pozwoli uniknąć ograniczeń podczas programowania.

  3. Jeśli domena aplikacji nie została jeszcze autoryzowana, dodaj ją do listy dozwolonych na stronie Uwierzytelnianie > Ustawienia w konsoli Firebase.

Wybieranie wzorca rejestracji

Możesz określić, czy Twoja aplikacja wymaga uwierzytelniania wielopoziomowego, a także jak i kiedy rejestrować użytkowników. Oto kilka typowych wzorców:

  • Zarejestruj drugi składnik uwierzytelniający użytkownika w ramach rejestracji. Użyj tej metody, jeśli Twoja aplikacja wymaga uwierzytelniania wielopoziomowego od wszystkich użytkowników.

  • Oferuj opcję pominięcia rejestracji drugiego składnika podczas rejestracji. Aplikacje, które chcą zachęcać do uwierzytelniania wielopoziomowego, ale nie wymagają go, mogą preferować to podejście.

  • Umożliwienie dodania drugiego czynnika na stronie zarządzania kontem lub profilem użytkownika, a nie na ekranie rejestracji. Minimalizuje to trudności podczas procesu rejestracji, a jednocześnie udostępnia uwierzytelnianie wielopoziomowe użytkownikom, dla których bezpieczeństwo jest priorytetem.

  • Wymagaj stopniowego dodawania drugiego etapu weryfikacji, gdy użytkownik chce uzyskać dostęp do funkcji o zwiększonych wymaganiach dotyczących bezpieczeństwa.

Rejestrowanie drugiego składnika

Aby zarejestrować nowy czynnik dodatkowy dla użytkownika:

  1. Ponownie uwierzytelnij użytkownika.

  2. Poproś użytkownika o wpisanie numeru telefonu.

  3. Uzyskiwanie sesji uwierzytelniania wielopoziomowego dla użytkownika:

    final multiFactorSession = await user.multiFactor.getSession();
    
  4. Zweryfikuj numer telefonu za pomocą sesji uwierzytelniania wieloskładnikowego i wywołań zwrotnych:

    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. Gdy kod SMS zostanie wysłany, poproś użytkownika o jego weryfikację:

    final credential = PhoneAuthProvider.credential(
      verificationId: verificationId,
      smsCode: smsCode,
    );
    
  6. Dokończ rejestrację:

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

Poniższy kod pokazuje kompletny przykład rejestracji drugiego czynnika:

  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/main/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: (_) {},
  );

Gratulacje! Udało Ci się zarejestrować drugi składnik uwierzytelniania dla użytkownika.

Logowanie użytkowników za pomocą drugiego składnika

Aby zalogować użytkownika za pomocą weryfikacji dwuskładnikowej przez SMS:

  1. Zaloguj użytkownika za pomocą pierwszego czynnika, a następnie przechwyć wyjątek FirebaseAuthMultiFactorException. Ten błąd zawiera obiekt resolver, którego możesz użyć do uzyskania zarejestrowanych drugich etapów weryfikacji użytkownika. Zawiera też sesję bazową, która potwierdza, że użytkownik został uwierzytelniony za pomocą pierwszego poziomu.

    Jeśli na przykład pierwszym składnikiem użytkownika był adres e-mail i hasło:

    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. Jeśli użytkownik ma zarejestrowanych kilka składników dodatkowych, zapytaj go, którego z nich chce użyć:

    final session = e.resolver.session;
    
    final hint = e.resolver.hints[selectedHint];
    
  3. Wyślij na telefon użytkownika wiadomość weryfikacyjną z podpowiedzią i sesją uwierzytelniania wielopoziomowego:

    await FirebaseAuth.instance.verifyPhoneNumber(
      multiFactorSession: session,
      multiFactorInfo: hint,
      verificationCompleted: (_) {},
      verificationFailed: (_) {},
      codeSent: (String verificationId, int? resendToken) async {
        // ...
      },
      codeAutoRetrievalTimeout: (_) {},
    );
    
  4. Zadzwoń pod numer resolver.resolveSignIn(), aby dokończyć uwierzytelnianie dodatkowe:

    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);
      }
    }
    

Poniższy kod przedstawia pełny przykład logowania użytkownika z uwierzytelnianiem wieloskładnikowym:

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

Gratulacje! Udało Ci się zalogować użytkownika za pomocą uwierzytelniania wielopoziomowego.

Co dalej?