Dodaj uwierzytelnianie wielopoziomowe TOTP do swojej aplikacji na Androida

Po przejściu na Uwierzytelnianie Firebase z Identity Platform możesz dodać do swojej aplikacji uwierzytelnianie wielopoziomowe z użyciem hasła jednorazowego (TOTP).

Uwierzytelnianie Firebase z Identity Platform umożliwia użycie TOTP jako dodatkowego czynnika w przypadku MFA. Gdy włączysz tę funkcję, użytkownicy próbujący zalogować się w aplikacji zobaczą prośbę o podanie hasła TOTP. Aby go wygenerować, należy użyć aplikacji uwierzytelniającej, która generuje prawidłowe kody TOTP, na przykład Google Authenticator.

Zanim zaczniesz

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

    • Uwierzytelnianie przez telefon
    • Anonimowe uwierzytelnianie
    • Niestandardowe tokeny uwierzytelniania
    • Centrum gier Apple
  2. Upewnij się, że Twoja aplikacja weryfikuje adresy e-mail użytkowników. MFA wymaga weryfikacji adresu e-mail. Dzięki temu niepowołane osoby nie będą mogły zarejestrować się w usłudze za pomocą adresu e-mail, który nie należą do nich, a następnie zablokują faktycznego właściciela adresu e-mail przez dodanie drugiego składnika.

  3. Zainstaluj pakiet SDK Firebase na Androida, jeśli jeszcze go nie masz.

    TOTP MFA jest obsługiwane tylko przez pakiet SDK do Androida w wersji 22.1.0 lub nowszej.

Włącz TOTP MFA

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

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

  1. Zainstaluj pakiet SDK Firebase Admin Node.js, jeśli jeszcze go nie masz.

    Protokół TOTP MFA jest obsługiwany 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ąsiednich przedziałów czasu, od 0 do 10 przedziałów czasu, od których przyjmuje się TOTP. Wartością domyślną jest 5.

      Wykonawcy TOTP zapewniają, że gdy 2 strony (weryfikator i walidator) generują hasła jednorazowe w tym samym przedziale czasu (zazwyczaj 30 sekund), generują to samo hasło. Jednak aby uwzględnić drgania zegara między stronami i czas reakcji pracowników, możesz skonfigurować usługę TOTP tak, aby akceptowała TOTP także z sąsiednich okien.

Aby włączyć uwierzytelnianie typu TOTP 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 czasu, od 0 do 10. Wartością domyślną jest 5.

    Wykonawcy TOTP zapewniają, że gdy 2 strony (weryfikator i walidator) generują hasła jednorazowe w tym samym przedziale czasu (zazwyczaj 30 sekund), generują to samo hasło. Jednak aby uwzględnić drgania zegara między stronami i czas reakcji pracowników, możesz skonfigurować usługę TOTP tak, aby akceptowała TOTP także z sąsiednich okien.

Wybierz wzorzec rejestracji

Możesz określić, czy Twoja aplikacja wymaga uwierzytelniania wielopoziomowego oraz jak i kiedy rejestrować użytkowników. Oto niektóre typowe wzorce:

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

  • Zaproponuj możliwość pominięcia uwierzytelniania dwuskładnikowego podczas rejestracji. Jeśli chcesz zachęcić użytkowników do korzystania z uwierzytelniania wielopoziomowego w swojej aplikacji, ale nie wymagać go, możesz wykorzystać tę metodę.

  • Daj możliwość dodania drugiego składnika na stronie zarządzania kontem lub profilem użytkownika zamiast na ekranie rejestracji. Pozwala to zminimalizować błędy podczas rejestracji i jednocześnie udostępniać uwierzytelnianie wielopoziomowe użytkownikom, którzy zwracają szczególną uwagę na bezpieczeństwo.

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

Rejestrowanie użytkowników w TOTP MFA

Po włączeniu MFA TOTP jako drugiego etapu w aplikacji zaimplementuj logikę po stronie klienta, aby rejestrować użytkowników w TOTP MFA:

  1. Ponownie uwierzytelnij użytkownika.

  2. Wygeneruj tajny 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 uwierzytelniania:

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

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

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

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

  4. Poproś użytkownika o wpisanie hasła TOTP wyświetlanego w aplikacji uwierzytelniającej 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 za pomocą TOTP MFA, użyj tego kodu:

  1. Wywołaj jedną z metod signInWith-, tak jak w przypadku braku MFA. (np. signInWithEmailAndPassword()). Jeśli metoda generuje 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 aplikacji powinien najpierw poprosić użytkownika o wybranie drugiego czynnika, którego chce użyć. Listę obsługiwanych drugich czynników możesz uzyskać, analizując właściwość hints instancji MultiFactorResolver:

    val enrolledFactors = exception.resolver.hints.map { it.displayName }
    
  3. Jeśli użytkownik zdecyduje się używać TOTP, poproś go o wpisanie tego hasła wyświetlanego w aplikacji uwierzytelniającej i użyj go do zalogowania:

    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 TOTP MFA

Ta sekcja opisuje, jak postępować w przypadku wyrejestrowania się przez użytkownika z TOTP MFA.

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

Aby wyrejestrować użytkownika, obsłużyć błąd i aktywować 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?