Thêm tính năng xác thực đa yếu tố TOTP vào ứng dụng web

Nếu đã nâng cấp lên tính năng Xác thực Firebase bằng Nền tảng nhận dạng, bạn có thể thêm mật khẩu một lần theo thời gian (TOTP) xác thực đa yếu tố (MFA) cho ứng dụng của bạn.

Xác thực Firebase với nền tảng danh tính cho phép bạn sử dụng TOTP làm yếu tố bổ sung cho MFA. Khi bật tính năng này, người dùng cố gắng đăng nhập vào ứng dụng của bạn sẽ thấy yêu cầu TOTP. Để tạo dữ liệu xác thực, họ phải sử dụng một ứng dụng xác thực có khả năng tạo mã TOTP hợp lệ, chẳng hạn như Google Authenticator.

Trước khi bắt đầu

  1. Hãy bật ít nhất một nhà cung cấp hỗ trợ MFA. Xin lưu ý rằng tất cả các nhà cung cấp ngoại trừ các tính năng hỗ trợ MFA hỗ trợ sau đây:

    • Xác thực điện thoại
    • Xác thực ẩn danh
    • Mã thông báo xác thực tuỳ chỉnh
    • Trung tâm trò chơi của Apple
  2. Đảm bảo ứng dụng của bạn xác minh địa chỉ email của người dùng. Tính năng xác thực đa yếu tố (MFA) yêu cầu có email xác minh. Thao tác này ngăn đối tượng độc hại đăng ký một dịch vụ bằng địa chỉ email họ không sở hữu và sau đó khoá chủ sở hữu của địa chỉ email đó bằng cách thêm yếu tố thứ hai.

  3. Nếu bạn chưa làm như vậy, hãy cài đặt SDK JavaScript của Firebase.

    TOTP MFA chỉ được hỗ trợ trên SDK Web mô-đun, phiên bản v9.19.1 và ở trên.

Bật TOTP MFA

Để bật TOTP làm yếu tố thứ hai, hãy sử dụng SDK dành cho quản trị viên hoặc gọi dự án điểm cuối REST của cấu hình.

Để dùng SDK dành cho quản trị viên, hãy làm như sau:

  1. Nếu bạn chưa làm như vậy, hãy cài đặt SDK Node.js dành cho quản trị viên của Firebase.

    TOTP MFA chỉ được hỗ trợ trên SDK Node.js dành cho quản trị viên Firebase phiên bản 11.6.0 và ở trên.

  2. Chạy lệnh sau:

    import { getAuth } from 'firebase-admin/auth';
    
    getAuth().projectConfigManager().updateProjectConfig(
    {
          multiFactorConfig: {
              providerConfigs: [{
                  state: "ENABLED",
                  totpProviderConfig: {
                      adjacentIntervals: NUM_ADJ_INTERVALS
                  }
              }]
          }
    })
    

    Thay thế đoạn mã sau:

    • NUM_ADJ_INTERVALS: Số cửa sổ liền kề khoảng thời gian ghi nhận TOTP từ 0 đến 10. Chiến lược phát hành đĩa đơn mặc định là 5.

      TOTP hoạt động bằng cách đảm bảo rằng khi hai bên (người chứng minh và người tạo OTP trong cùng một khoảng thời gian (thường là 30 giây) lâu), chúng sẽ tạo cùng một mật khẩu. Tuy nhiên, để phù hợp với đồng hồ giữa các bên và thời gian phản hồi của con người, bạn có thể định cấu hình TOTP để chấp nhận TOTP từ các cửa sổ liền kề.

Để bật TOTP MFA bằng API REST, hãy chạy lệnh sau:

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
            }
          }]
       }
    }'

Thay thế đoạn mã sau:

  • PROJECT_ID: Mã dự án.
  • NUM_ADJ_INTERVALS: Số khung thời gian khoảng thời gian, từ 0 đến 10. Giá trị mặc định là 5.

    TOTP hoạt động bằng cách đảm bảo rằng khi hai bên (người chứng minh và người tạo OTP trong cùng một khoảng thời gian (thường là 30 giây) lâu), chúng sẽ tạo cùng một mật khẩu. Tuy nhiên, để phù hợp với đồng hồ giữa các bên và thời gian phản hồi của con người, bạn có thể định cấu hình TOTP để chấp nhận TOTP từ các cửa sổ liền kề.

Chọn một hình thức đăng ký

Bạn có thể chọn xem ứng dụng của mình có cần xác thực đa yếu tố hay không, cũng như và thời điểm đăng ký người dùng. Sau đây là một số mẫu phổ biến:

  • Đăng ký yếu tố thứ hai của người dùng trong quá trình đăng ký. Sử dụng bản thảo này nếu ứng dụng của bạn yêu cầu xác thực đa yếu tố đối với tất cả người dùng.

  • Cung cấp lựa chọn có thể bỏ qua để đăng ký yếu tố thứ hai trong quá trình đăng ký. Nếu bạn muốn khuyến khích nhưng không yêu cầu xác thực đa yếu tố trong ứng dụng của mình, bạn có thể sử dụng phương pháp này.

  • Cho phép thêm yếu tố thứ hai từ tài khoản hoặc hồ sơ của người dùng trang quản lý thay vì màn hình đăng ký. Việc này giúp giảm thiểu rào cản trong quá trình đăng ký, trong khi vẫn thực hiện xác thực đa yếu tố dành cho người dùng nhạy cảm về bảo mật.

  • Yêu cầu thêm dần yếu tố thứ hai khi người dùng muốn truy cập có yêu cầu bảo mật cao hơn.

Đăng ký người dùng tham gia TOTP MFA

Sau khi bạn bật TOTP MFA làm yếu tố thứ hai cho ứng dụng của mình, hãy triển khai phía máy khách logic để đăng ký người dùng tham gia TOTP MFA:

  1. Nhập các lớp và hàm MFA bắt buộc:

    import {
      multiFactor,
      TotpMultiFactorGenerator,
      TotpSecret,
      getAuth,
    } from "firebase/auth";
    
  2. Xác thực lại người dùng.

  3. Tạo mã bí mật TOTP cho người dùng đã xác thực:

    // Generate a TOTP secret.
    const multiFactorSession = await multiFactor(currentUser).getSession();
    const totpSecret = await TotpMultiFactorGenerator.generateSecret(
      multiFactorSession
    );
    
  4. Hiển thị bí mật cho người dùng và nhắc họ nhập bí mật vào ứng dụng xác thực.

    Với nhiều ứng dụng xác thực, người dùng có thể nhanh chóng thêm các bí mật TOTP mới bằng cách quét mã QR biểu thị URI khoá tương thích với Google Authenticator. Để tạo mã QR cho mục đích này, hãy tạo URI bằng generateQrCodeUrl(), sau đó mã hoá mã bằng thư viện mã QR của lựa chọn. Ví dụ:

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

    Bất kể bạn có hiện mã QR hay không, hãy luôn hiện khoá bí mật để hỗ trợ ứng dụng xác thực không thể đọc mã QR:

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

    Sau khi người dùng thêm khoá bí mật của họ vào ứng dụng xác thực, ứng dụng sẽ khởi động tạo TOTP.

  5. Nhắc người dùng nhập TOTP xuất hiện trên ứng dụng xác thực của họ rồi sử dụng mã này để hoàn tất đăng ký 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);
    

Đăng nhập cho người dùng bằng yếu tố thứ hai

Để đăng nhập người dùng bằng TOTP MFA, hãy sử dụng mã sau:

  1. Nhập các lớp và hàm MFA bắt buộc:

    import {
        getAuth,
        getMultiFactorResolver,
        TotpMultiFactorGenerator,
    } from "firebase/auth";
    
  2. Gọi một trong các phương thức signInWith- như cách bạn thực hiện nếu không sử dụng MFA. (Ví dụ: signInWithEmailAndPassword().) Nếu phương thức này gửi ra auth/multi-factor-auth-required lỗi, hãy bắt đầu luồng MFA của ứng dụng.

    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. Trước tiên, quy trình MFA của ứng dụng phải nhắc người dùng chọn yếu tố thứ hai mà họ muốn sử dụng. Bạn có thể xem danh sách các yếu tố thứ hai được hỗ trợ bằng cách kiểm tra thuộc tính hints của một thực thể MultiFactorResolver:

    const mfaResolver = getMultiFactorResolver(getAuth(), error);
    const enrolledFactors = mfaResolver.hints.map(info => info.displayName);
    
  4. Nếu người dùng chọn sử dụng TOTP, hãy nhắc họ nhập TOTP được hiển thị trên ứng dụng xác thực của họ và sử dụng ứng dụng đó để đăng nhập:

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

Huỷ đăng ký TOTP MFA

Phần này mô tả cách xử lý việc người dùng huỷ đăng ký TOTP MFA.

Nếu người dùng đã đăng ký nhiều tùy chọn MFA và nếu họ hủy đăng ký từ tuỳ chọn được bật gần đây nhất, họ sẽ nhận được một auth/user-token-expired và đã đăng xuất. Người dùng phải đăng nhập lại và xác minh thông tin đăng nhập hiện có, chẳng hạn như địa chỉ email và mật khẩu.

Để huỷ đăng ký người dùng, xử lý lỗi và kích hoạt xác thực lại, hãy sử dụng mã sau:

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

Bước tiếp theo