Dodawanie uwierzytelniania wielopoziomowego TOTP do aplikacji na iOS

Jeśli korzystasz z wersji Firebase Authentication with Identity Platform, możesz dodać do aplikacji uwierzytelnianie wielopoziomowe (MFA) z hasłem jednorazowym generowanym na podstawie czasu (TOTP).

Firebase Authentication with Identity Platform umożliwia korzystanie z TOTP jako dodatkowego czynnika uwierzytelniania wieloskładnikowego. Gdy włączysz tę funkcję, użytkownicy, którzy spróbują zalogować się w aplikacji, zobaczą prośbę o wygenerowanie kodu 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. Zapobiega to rejestrowaniu się w usłudze za pomocą adresu e-mail, którego nie ma się na własność, a potem blokowaniu dostępu do niego rzeczywistemu właścicielowi adresu przez dodanie drugiego czynnika uwierzytelniania.

  3. Jeśli jeszcze tego nie zrobiono, zainstaluj pakiet SDK Firebase dla Apple.

    TOTP MFA jest obsługiwana tylko w pakiecie Apple SDK w wersji 10.12.0 lub nowszej i tylko na iOS.

Włączanie uwierzytelniania wieloskładniowego TOTP

Aby włączyć TOTP jako drugi czynnik, użyj 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.

    Protokół TOTP MFA jest obsługiwany tylko przez pakiet 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 czasowych, w których mogą być akceptowane żądania TOTP, 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 ze względu na różnice w czasie między stronami i czasie reakcji człowieka możesz skonfigurować usługę TOTP tak, aby akceptowała ona również TOTP z sąsiednich okien.

Aby włączyć uwierzytelnianie wieloskładnikowe TOTP za pomocą interfejsu REST API, wykonaj te czynności:

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ścią domyślną jest 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 ze względu na różnice w czasie między stronami i czasie reakcji człowieka możesz skonfigurować usługę TOTP tak, aby akceptowała ona również TOTP z sąsiednich okien.

Wybieranie schematu 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 uwierzytelniający użytkownika w ramach rejestracji. Użyj tej metody, jeśli aplikacja wymaga uwierzytelniania wielopoziomowego od wszystkich użytkowników.

  • Zaoferować możliwą do pominięcia opcję rejestracji drugiego składnika 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ć utrudnienia podczas procesu rejestracji, a jednocześnie umożliwić korzystanie z uwierzytelniania wielopoziomowego użytkownikom, którym zależy na bezpieczeństwie.

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

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.
    guard let mfaSession = try? await currentUser.multiFactor.session() else { return }
    guard let totpSecret = try? await TOTPMultiFactorGenerator.generateSecret(with: mfaSession) else { return }
    
    // Display the secret to the user and prompt them to enter it into their
    // authenticator app. (See the next step.)
    
  3. Wyświetl klucz tajny użytkownikowi i poproś go o jego wpisanie w aplikacji uwierzytelniającej:

    // Display this key:
    let 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(withQRCodeURL:):

    let otpAuthUri = totpSecret.generateQRCodeURL(
        withAccountName: currentUser.email ?? "default account",
        issuer: "Your App Name")
    totpSecret.openInOTPApp(withQRCodeURL: otpAuthUri)
    

    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.
    let verificationCode = // Code from user input.
    
    // Finalize the enrollment.
    let multiFactorAssertion = TOTPMultiFactorGenerator.assertionForEnrollment(
        with: totpSecret,
        oneTimePassword: verificationCode)
    do {
        try await currentUser.multiFactor.enroll(
            with: multiFactorAssertion,
            displayName: "TOTP")
    } catch {
        // Wrong or expired OTP. Re-prompt the user.
    }
    

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. Wywołaj jedną z metod signIn(with...:) tak, jakbyś nie używał uwierzytelniania wieloetapowego (np. signIn(withEmail:password:)). Jeśli metoda zwróci błąd o kodze secondFactorRequired, uruchom proces uwierzytelniania wieloetapowego w aplikacji.

    do {
        let authResult = try await Auth.auth().signIn(withEmail: email, password: password)
    
        // 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.)
    
        // ...
    } catch let error as AuthErrorCode where error.code == .secondFactorRequired {
        // Initiate your second factor sign-in flow. (See next step.)
        // ...
    } catch {
        // Other auth error.
        throw error
    }
    
  2. W ramach procesu uwierzytelniania dwuskładnikowego aplikacja powinna najpierw poprosić użytkownika o wybranie drugiego czynnika. Listę obsługiwanych drugich czynników możesz uzyskać, sprawdzając właściwość hints instancji MultiFactorResolver:

    let mfaKey = AuthErrorUserInfoMultiFactorResolverKey
    guard let resolver = error.userInfo[mfaKey] as? MultiFactorResolver else { return }
    let enrolledFactors = resolver.hints.map(\.displayName)
    
  3. Jeśli użytkownik zdecyduje się używać protokołu TOTP, poproś go o wpisanie TOTP wyświetlonego w aplikacji uwierzytelniającej i użycie go do logowania:

    let multiFactorInfo = resolver.hints[selectedIndex]
    switch multiFactorInfo.factorID {
    case TOTPMultiFactorID:
        let otpFromAuthenticator = // OTP typed by the user.
        let assertion = TOTPMultiFactorGenerator.assertionForSignIn(
            withEnrollmentID: multiFactorInfo.uid,
            oneTimePassword: otpFromAuthenticator)
        do {
            let authResult = try await resolver.resolveSignIn(with: assertion)
        } catch {
            // Wrong or expired OTP. Re-prompt the user.
        }
    default:
        return
    }
    

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 zarejestrował się w wielu opcjach MFA i wyrejestruje się przy użyciu ostatnio włączonej opcji, otrzyma komunikat auth/user-token-expired i zostanie wylogowany. Użytkownik musi się ponownie zalogować 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:

guard let currentUser = Auth.auth().currentUser else { return }

// Prompt the user to select a factor to unenroll, from this array:
currentUser.multiFactor.enrolledFactors

// ...

// Unenroll the second factor.
let multiFactorInfo = currentUser.multiFactor.enrolledFactors[selectedIndex]
do {
    try await currentUser.multiFactor.unenroll(with: multiFactorInfo)
} catch let error as AuthErrorCode where error.code == .invalidUserToken {
    // Second factor unenrolled, but the user was signed out. Re-authenticate
    // them.
}

Co dalej?