Dodaj uwierzytelnianie wielopoziomowe TOTP do swojej aplikacji internetowej

Jeśli masz subskrypcję 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 używanie TOTP jako dodatkowego składnika uwierzytelniania wieloskładnikowego. Gdy włączysz tę funkcję, użytkownicy próbujący zalogować się w Twojej aplikacji zobaczą prośbę o podanie kodu TOTP. Aby go wygenerować, użytkownik musi użyć aplikacji uwierzytelniającej, która potrafi generować prawidłowe kody TOTP, np. Google Authenticator.

Zanim zaczniesz

  1. Włącz co najmniej jednego dostawcę obsługującego MFA. Pamiętaj, że wszyscy dostawcy z wyjątkiem wymienionych poniżej obsługują uwierzytelnianie dwuskładnikowe:

    • Uwierzytelnianie przez telefon
    • Anonimowe uwierzytelnianie
    • Niestandardowe tokeny uwierzytelniania
    • Apple Game Center
  2. Upewnij się, że Twoja aplikacja weryfikuje adresy e-mail użytkowników. MFA wymaga weryfikacji adresu e-mail. Zapobiega to rejestrowaniu się nieuczciwych podmiotów w usłudze za pomocą adresu e-mail, którego nie są właścicielami, a następnie blokowaniu dostępu do tego adresu e-mail przez dodanie drugiego czynnika.

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

    Wieloskładnikowe uwierzytelnianie TOTP jest obsługiwane tylko w modułowym pakiecie SDK na potrzeby internetu w wersji 9.19.1 i nowszych.

Włączanie MFA TOTP

Aby włączyć TOTP jako drugi poziom uwierzytelniania, 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 tego nie zrobiono, zainstaluj pakiet Firebase Admin Node.js SDK.

    Wieloskładnikowe uwierzytelnianie TOTP jest obsługiwane tylko w pakiecie Firebase Admin Node.js SDK w wersji 11.6.0 i nowszych.

  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, z których można akceptować kody TOTP. Wartość od zera do dziesięciu. Wartością domyślną jest 5.

      TOTP działają w ten sposób, że gdy 2 strony (weryfikujący i sprawdzający) generują hasła OTP w tym samym przedziale czasu (zwykle 30 sekund), otrzymują to samo hasło. Aby jednak uwzględnić odchylenia zegara między stronami i czas reakcji użytkownika, możesz skonfigurować usługę TOTP tak, aby akceptowała też kody TOTP z sąsiednich okien.

Aby włączyć uwierzytelnianie wieloskładnikowe TOTP za pomocą interfejsu REST API, wykonaj 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 zera do dziesięciu. Wartość domyślna to 5.

    TOTP działają w ten sposób, że gdy 2 strony (weryfikujący i sprawdzający) generują hasła OTP w tym samym przedziale czasu (zwykle 30 sekund), otrzymują to samo hasło. Aby jednak uwzględnić odchylenia zegara między stronami i czas reakcji użytkownika, możesz skonfigurować usługę TOTP tak, aby akceptowała też kody TOTP z sąsiednich okien.

Wybierz wzorzec rejestracji

Możesz określić, czy aplikacja wymaga uwierzytelniania wielopoziomowego, a także 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 Twoja aplikacja wymaga uwierzytelniania wielopoziomowego od wszystkich użytkowników.

  • Podczas rejestracji oferuj opcję pominięcia rejestracji drugiego składnika. Jeśli chcesz zachęcać do uwierzytelniania wielopoziomowego w aplikacji, ale nie chcesz go wymagać, możesz zastosować to podejście.

  • Umożliwia dodanie 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, którzy dbają o bezpieczeństwo.

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

Rejestrowanie użytkowników w MFA opartej na TOTP

Po włączeniu uwierzytelniania wielopoziomowego TOTP jako drugiego poziomu uwierzytelniania w aplikacji zaimplementuj logikę po stronie klienta, aby umożliwić użytkownikom rejestrację w uwierzytelnianiu wielopoziomowym TOTP:

  1. Zaimportuj wymagane klasy i funkcje uwierzytelniania dwuskładnikowego:

    import {
      multiFactor,
      TotpMultiFactorGenerator,
      TotpSecret,
      getAuth,
    } from "firebase/auth";
    
  2. Ponownie uwierzytelnij użytkownika.

  3. Wygeneruj tajny klucz TOTP dla uwierzytelnionego użytkownika:

    // Generate a TOTP secret.
    const multiFactorSession = await multiFactor(currentUser).getSession();
    const totpSecret = await TotpMultiFactorGenerator.generateSecret(
      multiFactorSession
    );
    
  4. Wyświetl tajny klucz użytkownikowi i poproś go o wpisanie go w aplikacji uwierzytelniającej.

    Wielu użytkowników aplikacji uwierzytelniających może szybko dodawać nowe klucze TOTP, skanując kod QR reprezentujący identyfikator URI klucza zgodny z aplikacją Google Authenticator. Aby wygenerować kod QR w tym celu, wygeneruj identyfikator URI za pomocą generateQrCodeUrl(), a następnie zakoduj go za pomocą wybranej biblioteki kodów QR. Przykład:

    const totpUri = totpSecret.generateQrCodeUrl(
        currentUser.email,
        "Your App's Name"
    );
    await QRExampleLib.toCanvas(totpUri, qrElement);
    

    Niezależnie od tego, czy wyświetlasz kod QR, zawsze wyświetlaj klucz tajny, aby obsługiwać aplikacje do uwierzytelniania, które nie mogą odczytać kodów QR:

    // Also display this key:
    const secret = totpSecret.secretKey;
    

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

  5. Poproś użytkownika o wpisanie kodu TOTP wyświetlanego w aplikacji uwierzytelniającej i użyj go, aby dokończyć rejestrację w usłudze MFA:

    // Ask the user for a verification code from the authenticator app.
    const verificationCode = // Code from user input.
    
    // Finalize the enrollment.
    const multiFactorAssertion = TotpMultiFactorGenerator.assertionForEnrollment(
      totpSecret,
      verificationCode
    );
    await multiFactor(currentUser).enroll(multiFactorAssertion, mfaDisplayName);
    

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

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

  1. Zaimportuj wymagane klasy i funkcje uwierzytelniania dwuskładnikowego:

    import {
        getAuth,
        getMultiFactorResolver,
        TotpMultiFactorGenerator,
    } from "firebase/auth";
    
  2. Wywołaj jedną z metod signInWith w taki sam sposób, jakby MFA nie była używana. (Na przykład signInWithEmailAndPassword()). Jeśli metoda zwróci błąd auth/multi-factor-auth-required, rozpocznij proces uwierzytelniania wieloskładnikowego w aplikacji.

    try {
        const userCredential = await signInWithEmailAndPassword(
            getAuth(),
            email,
            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 (error) {
        switch (error.code) {
            case "auth/multi-factor-auth-required":
                // Initiate your second factor sign-in flow. (See next step.)
                // ...
                break;
            case ...:  // Handle other errors, such as wrong passwords.
                break;
        }
    }
    
  3. Proces uwierzytelniania dwuskładnikowego w aplikacji powinien najpierw wyświetlić użytkownikowi prośbę 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:

    const mfaResolver = getMultiFactorResolver(getAuth(), error);
    const enrolledFactors = mfaResolver.hints.map(info => info.displayName);
    
  4. Jeśli użytkownik zdecyduje się użyć TOTP, poproś go o wpisanie hasła TOTP wyświetlanego w aplikacji uwierzytelniającej i użycie go do zalogowania się:

    switch (mfaResolver.hints[selectedIndex].factorId) {
        case TotpMultiFactorGenerator.FACTOR_ID:
            const otpFromAuthenticator = // OTP typed by the user.
            const multiFactorAssertion =
                TotpMultiFactorGenerator.assertionForSignIn(
                    mfaResolver.hints[selectedIndex].uid,
                    otpFromAuthenticator
                );
            try {
                const userCredential = await mfaResolver.resolveSignIn(
                    multiFactorAssertion
                );
                // Successfully signed in!
            } catch (error) {
                // Invalid or expired OTP.
            }
            break;
        case PhoneMultiFactorGenerator.FACTOR_ID:
            // Handle SMS second factor.
            break;
        default:
            // Unsupported second factor?
            break;
    }
    

Wyrejestrowanie z MFA TOTP

W tej sekcji opisaliśmy, jak postępować w przypadku, gdy użytkownik wyrejestruje się z wieloskładnikowego uwierzytelniania TOTP.

Jeśli użytkownik zarejestrował się w kilku opcjach MFA i zrezygnuje z najnowszej z nich, otrzyma auth/user-token-expired i zostanie wylogowany. Użytkownik musi ponownie się zalogować i zweryfikować dotychczasowe dane logowania, np. adres e-mail i hasło.

Aby wyrejestrować użytkownika, obsłużyć błąd i ponownie uwierzytelnić, użyj tego kodu:

import {
    EmailAuthProvider,
    TotpMultiFactorGenerator,
    getAuth,
    multiFactor,
    reauthenticateWithCredential,
} from "firebase/auth";

try {
    // Unenroll from TOTP MFA.
    await multiFactor(currentUser).unenroll(mfaEnrollmentId);
} catch  (error) {
    if (error.code === 'auth/user-token-expired') {
        // 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.

        const credential = EmailAuthProvider.credential(email, password);
        await reauthenticateWithCredential(
            currentUser,
            credential
        );
    }
}

Co dalej?