Dodawanie uwierzytelniania wielopoziomowego TOTP do aplikacji na Androida

Jeśli korzystasz już z Firebase Authentication with Identity Platform, możesz dodać do swojej aplikacji uwierzytelnianie wielopoziomowe z hasłem jednorazowym (TOTP).

Firebase Authentication with Identity Platform umożliwia korzystanie z TOTP jako dodatkowego czynnika uwierzytelniania wieloskładnikowego. Gdy włączysz tę funkcję, użytkownicy próbujący zalogować się w Twojej aplikacji zobaczą żądanie hasła TOTP. Aby wygenerować kod, muszą użyć aplikacji uwierzytelniającej, która umożliwia generowanie prawidłowych kodów TOTP, takiej jak Google Authenticator.

Zanim zaczniesz

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

    • uwierzytelnianie przez telefon,
    • Anonimowe uwierzytelnianie
    • Niestandardowe tokeny uwierzytelniania
    • Apple Game Center
  2. Upewnij się, że aplikacja weryfikuje adresy e-mail użytkowników. MFA wymaga weryfikacji adresu e-mail. Dzięki temu hakerzy nie będą mogli zarejestrować się w usłudze za pomocą należącego do nich adresu e-mail, a potem zablokować rzeczywistego właściciela adresu e-mail przez dodanie drugiego składnika.

  3. Jeśli jeszcze tego nie zrobiono, zainstaluj pakiet SDK Firebase na Androida.

    TOTP MFA jest obsługiwana tylko w pakiecie Android SDK w wersji 22.1.0 lub nowszej.

Włączanie uwierzytelniania wieloskładniowego TOTP

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

Aby korzystać z Admin SDK:

  1. Jeśli jeszcze tego nie zrobiono, zainstaluj pakiet SDK Firebase Admin Node.js.

    TOTP MFA jest obsługiwana 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 mają być akceptowane tokeny TOTP, od 0 do 10. Wartość domyślna to 5.

      TOTP działają w taki sposób, że gdy obie strony (weryfikator i weryfikator) generują hasło jednorazowe w tym samym oknie czasowym (zwykle 30 sekund), generują to samo hasło. Jednak aby uwzględnić dryf zegarowy między podmiotami i czas reakcji ludzi, możesz skonfigurować usługę TOTP tak, aby akceptowała także TOTP z sąsiednich okien.

Aby włączyć TOTP MFA za pomocą interfejsu API REST, 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. Wartość domyślna to 5.

    Hasła TOTP muszą mieć pewność, że gdy 2 strony (tester i walidator) wygenerują hasła jednorazowe w tym samym przedziale czasu (zwykle 30 sekund), wygenerują to samo hasło. Jednak aby uwzględnić dryf zegarowy między podmiotami i czas reakcji ludzi, możesz skonfigurować usługę TOTP tak, aby akceptowała także TOTP z sąsiednich okien.

Wybieranie schematu rejestracji

Możesz wybrać, czy Twoja aplikacja wymaga uwierzytelniania wielopoziomowego, a także sposób i moment rejestracji 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 aplikacja wymaga uwierzytelniania wielopoziomowego od wszystkich użytkowników.

  • Zaoferuj opcję pominięcia rejestracji drugiego czynnika podczas rejestracji. Jeśli chcesz zachęcać użytkowników do korzystania z uwierzytelniania wielopoziomowego, ale nie wymagać tego, możesz zastosować to rozwiązanie.

  • Dodanie możliwości dodawania drugiego czynnika na koncie użytkownika lub na stronie zarządzania profilem zamiast na ekranie rejestracji. Pozwala to zminimalizować trudności podczas procesu rejestracji, a jednocześnie udostępnia uwierzytelnianie wielopoziomowe użytkownikom, którzy przykładają dużą wagę do bezpieczeństwa.

  • Wymagaj stopniowego dodawania drugiego czynnika, gdy użytkownik chce uzyskać dostęp do funkcji o większych wymaganiach bezpieczeństwa.

Rejestrowanie użytkowników w usługach MFA TOTP

Po włączeniu uwierzytelniania wieloskładnikowego TOTP jako drugiego poziomu uwierzytelniania w aplikacji wprowadź logikę po stronie klienta, aby rejestrować użytkowników w ramach uwierzytelniania wieloskładnikowego TOTP:

  1. Ponownie uwierzytelnij użytkownika.

  2. Wygeneruj klucz 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 użytkownikowi tajny klucz 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 adres URI klucza zgodny z 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 klucz tajny do aplikacji uwierzytelniającej, aplikacja zacznie generować kody TOTP.

  4. Poproś użytkownika o wpisanie 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 zalogować użytkowników przy użyciu uwierzytelniania wieloskładniowego TOTP, użyj tego kodu:

  1. Zadzwoń do nas, korzystając z jednej z metod signInWith, tak jakbyś nie używał MFA. (np. signInWithEmailAndPassword()). Jeśli metoda zgłasza 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. W ramach procesu uwierzytelniania dwuskładnikowego aplikacja powinna najpierw poprosić użytkownika o wybranie drugiego czynnika. Listę obsługiwanych drugich czynników znajdziesz, sprawdzając właściwość hints instancji MultiFactorResolver:

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

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

Wyrejestrowanie z wielostopniowej weryfikacji tożsamości z użyciem klucza bezpieczeństwa TOTP

W tej sekcji opisano, jak postępować w przypadku wyrejestrowania użytkownika z wielostopniowej weryfikacji tożsamości TOTP.

Jeśli użytkownik korzysta z kilku opcji uwierzytelniania wielopoziomowego i rezygnuje z najnowszej z nich, otrzyma auth/user-token-expiredi zostanie wylogowany. Użytkownik musi zalogować się ponownie i potwierdzić swoje dotychczasowe dane logowania, np. adres e-mail i hasło.

Aby anulować rezygnację użytkownika, obsłużyć błąd i wywołać ponowne uwierzytelnianie, 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?