Dodaj uwierzytelnianie wielopoziomowe TOTP do swojej aplikacji internetowej

Jeśli masz już Firebase Authentication with Identity Platform, możesz dodać do swojej aplikacji uwierzytelnianie wielopoziomowe (MFA) za pomocą haseł jednorazowych opartych na czasie (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 je wygenerować, muszą użyć aplikacji uwierzytelniającej, która potrafi 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 MFA jest obsługiwane przez wszystkich dostawców z wyjątkiem tych:

    • 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ę w usłudze przez złośliwych użytkowników za pomocą adresu e-mail, który nie należy do nich, a następnie blokowaniu dostępu do tego adresu e-mail przez dodanie drugiego składnika.

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

    MFA za pomocą TOTP jest obsługiwane tylko w przypadku modułowego pakietu Web SDK w wersji 9.19.1 lub nowszej.

Włączanie MFA za pomocą TOTP

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

Aby używać pak0/}, wykonaj te czynności:Admin SDK

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

    MFA za pomocą TOTP jest obsługiwane tylko w przypadku pakietu Firebase Admin Node.js SDK 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, z których mają być akceptowane TOTP, od zera do dziesięciu. Domyślna wartość to 5.

      TOTP działają w ten sposób, że gdy 2 strony (sprawdzający i weryfikator) generują OTP w tym samym przedziale czasu (zwykle 30 sekund), generują to samo hasło. Aby jednak uwzględnić różnice w czasie między stronami i czas reakcji człowieka, możesz skonfigurować usługę TOTP tak, aby akceptowała też TOTP z sąsiednich przedziałów.

Aby włączyć MFA za pomocą TOTP przy użyciu 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 czasu, od zera do dziesięciu. Domyślna wartość to 5.

    TOTP działają w ten sposób, że gdy 2 strony (sprawdzający i weryfikator) generują OTP w tym samym przedziale czasu (zwykle 30 sekund), generują to samo hasło. Aby jednak uwzględnić różnice w czasie między stronami i czas reakcji człowieka, możesz skonfigurować usługę TOTP tak, aby akceptowała też TOTP z sąsiednich przedziałów.

Wybieranie wzorca rejestracji

Możesz wybrać, czy Twoja 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 w przypadku wszystkich użytkowników.

  • Podczas rejestracji zaproponuj opcję rejestracji drugiego składnika, którą można pominąć. Jeśli chcesz zachęcać do korzystania z uwierzytelniania wielopoziomowego w swojej aplikacji, ale nie chcesz go wymagać, możesz zastosować to podejście.

  • Zamiast na ekranie rejestracji umożliw użytkownikom dodawanie drugiego składnika na stronie zarządzania kontem lub profilem. Minimalizuje to trudności podczas procesu rejestracji, a jednocześnie udostępnia uwierzytelnianie wielopoziomowe użytkownikom, którzy dbają o bezpieczeństwo.

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

Rejestrowanie użytkowników w MFA za pomocą TOTP

Gdy włączysz MFA za pomocą TOTP jako drugi składnik w swojej aplikacji, zaimplementuj logikę po stronie klienta, aby zarejestrować użytkowników w MFA za pomocą TOTP:

  1. Zaimportuj wymagane klasy i funkcje MFA:

    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.

    W przypadku wielu aplikacji uwierzytelniających użytkownicy mogą szybko dodawać nowe tajne klucze TOTP, skanując kod QR, który reprezentuje adres URI klucza zgodny z Google Authenticator. Aby wygenerować kod QR w tym celu, wygeneruj adres URI za pomocą funkcji 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 tajny klucz, aby obsługiwać aplikacje uwierzytelniające, które nie mogą odczytywać kodów QR:

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

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

  5. Poproś użytkownika o wpisanie TOTP wyświetlanego w aplikacji uwierzytelniającej i użycie go do dokończenia rejestracji w 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 logować użytkowników za pomocą MFA za pomocą TOTP, użyj tego kodu:

  1. Zaimportuj wymagane klasy i funkcje MFA:

    import {
        getAuth,
        getMultiFactorResolver,
        TotpMultiFactorGenerator,
    } from "firebase/auth";
    
  2. Wywołaj jedną z metod signInWith- tak jak w przypadku, gdy nie używasz MFA. (Na przykład signInWithEmailAndPassword().) Jeśli metoda zgłosi błąd auth/multi-factor-auth-required, uruchom przepływ MFA w swojej 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. Przepływ MFA w Twojej 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:

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

Wyrejestrowywanie się z MFA za pomocą TOTP

W tej sekcji opisujemy, jak obsługiwać wyrejestrowywanie się użytkownika z MFA za pomocą TOTP.

Jeśli użytkownik zarejestrował się w kilku opcjach MFA i wyrejestruje się z ostatnio włączonej opcji, otrzyma błąd 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:

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?