Dodawanie uwierzytelniania wielopoziomowego TOTP do aplikacji na Androida

Jeśli masz Firebase Authentication with Identity Platform, możesz dodać do aplikacji uwierzytelnianie wielopoziomowe (MFA) za pomocą jednorazowego hasła czasowego (TOTP).

Firebase Authentication with Identity Platform umożliwia używanie TOTP jako dodatkowego składnika MFA. Gdy włączysz tę funkcję, użytkownicy próbujący zalogować się w Twojej aplikacji zobaczą prośbę o podanie TOTP. Aby go wygenerować, muszą użyć aplikacji uwierzytelniającej, która może generować prawidłowe kody TOTP, np. Google Authenticator.

Zanim zaczniesz

  1. Włącz co najmniej 1 dostawcę, który obsługuje MFA. Pamiętaj, że wszyscy dostawcy z wyjątkiem wymienionych poniżej obsługują MFA:

    • Uwierzytelnianie za pomocą telefonu
    • Anonimowe uwierzytelnianie
    • Niestandardowe tokeny uwierzytelniania
    • Apple Game Center
  2. Upewnij się, że aplikacja weryfikuje adresy e-mail użytkowników. Uwierzytelnianie wieloskładnikowe wymaga potwierdzenia adresu e-mail. Zapobiega to rejestrowaniu się nieuczciwych podmiotów w usłudze przy użyciu adresu e-mail, który nie należy do nich, a następnie blokowaniu rzeczywistego właściciela adresu e-mail przez dodanie drugiego składnika.

  3. Jeśli nie masz jeszcze zainstalowanego pakietu SDK Firebase na Androida, zainstaluj go.

    Uwierzytelnianie wieloskładnikowe TOTP jest obsługiwane tylko w pakiecie Android SDK w wersji 22.1.0 i nowszych.

Włączanie MFA z użyciem TOTP

Aby włączyć TOTP jako drugi składnik, użyj Admin SDK lub wywołaj punkt końcowy REST konfiguracji projektu.

Aby użyć Admin SDK, wykonaj te czynności:

  1. Jeśli jeszcze nie masz zainstalowanego pakietu Firebase Admin Node.js SDK, zainstaluj go.

    Uwierzytelnianie wieloskładnikowe TOTP jest obsługiwane tylko w pakiecie SDK Firebase Admin Node.js w wersji 11.6.0 lub nowszej.

  2. Wykonaj zapytanie:

    import { getAuth } from 'firebase-admin/auth';
    
    getAuth().projectConfigManager().updateProjectConfig(
    {
          multiFactorConfig: {
              providerConfigs: [{
                  state: "ENABLED",
                  totpProviderConfig: {
                      adjacentIntervals: NUM_ADJ_INTERVALS
                  }
              }]
          }
    })
    

    Zastąp następujące elementy:

    • NUM_ADJ_INTERVALS: liczba sąsiadujących przedziałów czasowych, z których akceptowane są kody TOTP (od 0 do 10). Wartość domyślna to 5.

      Kody TOTP działają w taki sposób, że gdy 2 strony (osoba potwierdzająca i weryfikator) generują hasła jednorazowe w tym samym przedziale czasowym (zwykle 30 sekund), generują to samo hasło. Aby jednak uwzględnić rozbieżności zegara między stronami i czas reakcji człowieka, możesz skonfigurować usługę TOTP tak, aby akceptowała również kody TOTP z sąsiednich okien.

Aby włączyć uwierzytelnianie wieloskładnikowe TOTP za pomocą interfejsu REST API, uruchom to polecenie:

curl -X PATCH "https://identitytoolkit.googleapis.com/admin/v2/projects/PROJECT_ID/config?updateMask=mfa" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json" \
    -H "X-Goog-User-Project: PROJECT_ID" \
    -d \
    '{
        "mfa": {
          "providerConfigs": [{
            "state": "ENABLED",
            "totpProviderConfig": {
              "adjacentIntervals": NUM_ADJ_INTERVALS
            }
          }]
       }
    }'

Zastąp następujące elementy:

  • PROJECT_ID: identyfikator projektu.
  • NUM_ADJ_INTERVALS: liczba przedziałów czasowych od 0 do 10. Domyślna wartość to 5.

    Kody TOTP działają w taki sposób, że gdy 2 strony (osoba potwierdzająca i weryfikator) generują hasła jednorazowe w tym samym przedziale czasowym (zwykle 30 sekund), generują to samo hasło. Aby jednak uwzględnić rozbieżności zegara między stronami i czas reakcji człowieka, możesz skonfigurować usługę TOTP tak, aby akceptowała również kody TOTP z sąsiednich okien.

Wybieranie wzorca rejestracji

Możesz wybrać, czy aplikacja ma wymagać uwierzytelniania wielopoziomowego, oraz określić, jak i kiedy rejestrować użytkowników. Oto kilka typowych wzorców:

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

  • Zaoferuj opcję pominięcia rejestracji drugiego składnika podczas rejestracji. Możesz użyć tego podejścia, jeśli chcesz zachęcać do uwierzytelniania wielopoziomowego w aplikacji, ale nie chcesz go wymagać.

  • Zapewnij możliwość dodania drugiego składnika na stronie zarządzania kontem lub profilem użytkownika, a nie na ekranie rejestracji. Pozwala to zminimalizować problemy podczas procesu rejestracji, a jednocześnie udostępnia uwierzytelnianie wielopoziomowe użytkownikom, którym zależy na bezpieczeństwie.

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

Rejestrowanie użytkowników w MFA z TOTP

Po włączeniu uwierzytelniania wielopoziomowego TOTP jako drugiego składnika w aplikacji zaimplementuj logikę po stronie klienta, aby rejestrować użytkowników w uwierzytelnianiu wielopoziomowym TOTP:

  1. Ponownie uwierzytelnij użytkownika.

  2. Wygeneruj obiekt tajny TOTP dla uwierzytelnionego użytkownika:

    // Generate a TOTP secret.
    Firebase.auth.currentUser.multiFactor.session
        .addOnSuccessListener { multiFactorSession ->
            TotpMultiFactorGenerator.generateSecret(multiFactorSession)
                .addOnSuccessListener { totpSecret ->
                    // Display the secret to the user and prompt them to
                    // enter it into their authenticator app. (See the next
                    // step.)
                }
        }
    
  3. Wyświetl klucz użytkownikowi i poproś go o wpisanie go w aplikacji uwierzytelniającej:

    // Display this key:
    val secret = totpSecret.sharedSecretKey
    

    Oprócz wyświetlania klucza tajnego możesz spróbować automatycznie dodać go do domyślnej aplikacji uwierzytelniającej na urządzeniu. Aby to zrobić, wygeneruj identyfikator URI klucza zgodny z aplikacją Google Authenticator i przekaż go do openInOtpApp():

    val qrCodeUri = totpSecret.generateQrCodeUrl(
        currentUser.email ?: "default account",
        "Your App Name")
    totpSecret.openInOtpApp(qrCodeUri)
    

    Gdy użytkownik doda swój klucz tajny do aplikacji uwierzytelniającej, zacznie ona generować kody TOTP.

  4. Poproś użytkownika o wpisanie kodu TOTP wyświetlanego przez aplikację uwierzytelniającą i użyj go do sfinalizowania rejestracji MFA:

    // Ask the user for a verification code from the authenticator app.
    val verificationCode = // Code from user input.
    
    // Finalize the enrollment.
    val multiFactorAssertion = TotpMultiFactorGenerator
        .getAssertionForEnrollment(totpSecret, verificationCode)
    Firebase.auth.currentUser.multiFactor.enroll(multiFactorAssertion, "TOTP")
        .addOnSuccessListener {
            // Enrollment complete.
        }
    

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

Aby logować użytkowników za pomocą uwierzytelniania wieloskładnikowego TOTP, użyj tego kodu:

  1. Wywołaj jedną z metod signInWith- tak jakbyś nie używał MFA. (np. signInWithEmailAndPassword()). Jeśli metoda wygeneruje błąd FirebaseAuthMultiFactorException, uruchom proces MFA aplikacji.

    Firebase.auth.signInWithEmailAndPassword(email, password)
        .addOnSuccessListener { result ->
            // If the user is not enrolled with a second factor and provided valid
            // credentials, sign-in succeeds.
    
            // (If your app requires MFA, this could be considered an error
            // condition, which you would resolve by forcing the user to enroll a
            // second factor.)
    
            // ...
        }
        .addOnFailureListener { exception ->
            when (exception) {
                is FirebaseAuthMultiFactorException -> {
                    // Initiate your second factor sign-in flow. (See next step.)
                    // ...
                }
            }
        }
    
  2. Proces MFA w aplikacji powinien najpierw poprosić użytkownika o wybranie drugiego składnika, którego chce użyć. Listę obsługiwanych drugich składników możesz uzyskać, sprawdzając właściwość hints instancji MultiFactorResolver:

    val enrolledFactors = exception.resolver.hints.map { it.displayName }
    
  3. Jeśli użytkownik zdecyduje się na użycie TOTP, poproś go o wpisanie kodu TOTP wyświetlanego w aplikacji uwierzytelniającej i użycie go do zalogowania się:

    when (exception.resolver.hints[selectedIndex].factorId) {
        TotpMultiFactorGenerator.FACTOR_ID -> {
            val otpFromAuthenticator = // OTP typed by the user.
            val assertion = TotpMultiFactorGenerator.getAssertionForSignIn(
                exception.resolver.hints[selectedIndex].uid,
                otpFromAuthenticator
            )
            exception.resolver.resolveSignIn(assertion)
                .addOnSuccessListener { result ->
                    // Successfully signed in!
                }
                .addOnFailureListener { resolveError ->
                    // Invalid or expired OTP.
                }
        }
        PhoneMultiFactorGenerator.FACTOR_ID -> {
            // Handle SMS second factor.
        }
    }
    

Wyrejestrowywanie z uwierzytelniania wielopoziomowego za pomocą TOTP

W tej sekcji opisano, jak postępować, gdy użytkownik wyrejestruje się z uwierzytelniania wieloskładnikowego TOTP.

Jeśli użytkownik zarejestrował się w wielu opcjach MFA i wyrejestruje się z ostatnio włączonej opcji, otrzyma auth/user-token-expired i zostanie wylogowany. Użytkownik musi zalogować się ponownie i zweryfikować swoje dotychczasowe dane logowania, np. adres e-mail i hasło.

Aby wyrejestrować użytkownika, obsłużyć błąd i wywołać ponowne uwierzytelnienie, użyj tego kodu:

Firebase.auth.currentUser.multiFactor.unenroll(mfaEnrollmentId)
    .addOnSuccessListener {
        // Second factor unenrolled.
    }
    .addOnFailureListener { exception ->
        when (exception) {
            is FirebaseAuthInvalidUserException -> {
                // Second factor unenrolled. If the user was signed out, re-authenticate
                // them.

                // For example, if they signed in with a password, prompt them to
                // provide it again, then call `reauthenticateWithCredential()` as shown
                // below.
                val credential = EmailAuthProvider.getCredential(email, password)
                currentUser.reauthenticate(credential)
                    .addOnSuccessListener { 
                        // Success!
                    }
                    .addOnFailureListener { 
                        // Bad email address and password combination.
                    }
            }
        }
    }

Co dalej?