웹 앱에 TOTP 다중 인증(MFA) 추가

Firebase Authentication with Identity Platform으로 업그레이드했다면 시간 기반 일회용 비밀번호(TOTP) 다중 인증(MFA)을 앱에 추가할 수 있습니다.

Firebase Authentication with Identity Platform을 사용하면 TOTP를 MFA의 추가 요소로 사용할 수 있습니다. 이 기능을 사용 설정하면 앱에 로그인하려는 사용자에게 TOTP 요청이 표시됩니다. 이를 생성하려면 Google OTP와 같이 유효한 TOTP 코드를 생성할 수 있는 OTP 앱을 사용해야 합니다.

시작하기 전에

  1. MFA를 지원하는 하나 이상의 제공업체를 사용 설정하세요. 다음을 제외한 모든 제공업체는 MFA를 지원합니다.

    • 전화 인증
    • 익명 인증
    • 커스텀 인증 토큰
    • Apple Game Center
  2. 앱에서 사용자 이메일 주소를 인증해야 합니다. MFA를 사용하려면 이메일 인증이 필요합니다. 이를 통해 악의적인 행위자가 자신이 소유하지 않은 이메일 주소에 서비스를 등록한 후 두 번째 단계를 추가하여 이메일 주소의 실제 소유자의 접근을 막는 일을 방지할 수 있습니다.

  3. 아직 설치하지 않았다면 Firebase JavaScript SDK를 설치합니다.

    TOTP MFA는 모듈식 웹 SDK 버전 v9.19.1 이상에서만 지원됩니다.

TOTP MFA 사용 설정

TOTP를 두 번째 단계로 사용 설정하려면 Admin SDK를 사용하거나 프로젝트 구성 REST 엔드포인트를 호출합니다.

Admin SDK를 사용하려면 다음 단계를 따르세요.

  1. 아직 설치하지 않았다면 Firebase Admin Node.js SDK를 설치합니다.

    TOTP MFA는 Firebase Admin Node.js SDK 버전 11.6.0 이상에서만 지원됩니다.

  2. 다음을 실행합니다.

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

    다음을 바꿉니다.

    • NUM_ADJ_INTERVALS: TOTP를 수락할 인접한 기간 간격의 수(0~10)입니다. 기본값은 5입니다.

      TOTP는 두 당사자(증명자 및 검사자)가 동일한 기간(일반적으로 30초) 내에 OTP를 생성할 때 동일한 비밀번호를 생성하도록 하는 방식으로 작동합니다. 그러나 당사자 간의 클럭 드리프트와 사람의 응답 시간을 수용하기 위해 TOTP 서비스가 인접한 기간의 TOTP도 허용하도록 구성할 수 있습니다.

REST API를 사용하여 TOTP MFA를 사용 설정하려면 다음을 실행합니다.

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

다음을 바꿉니다.

  • PROJECT_ID: 프로젝트 ID
  • NUM_ADJ_INTERVALS: 기간 간격(0~10). 기본값은 5입니다.

    TOTP는 두 당사자(증명자 및 검사자)가 동일한 기간(일반적으로 30초) 내에 OTP를 생성할 때 동일한 비밀번호를 생성하도록 하는 방식으로 작동합니다. 그러나 당사자 간의 클럭 드리프트와 사람의 응답 시간을 수용하기 위해 TOTP 서비스가 인접한 기간의 TOTP도 허용하도록 구성할 수 있습니다.

등록 패턴 선택

앱에 다중 인증(MFA)이 필요한지 여부 및 사용자 등록 방법과 시기를 선택할 수 있습니다. 몇몇 일반적인 패턴은 다음과 같습니다.

  • 등록 시 사용자의 두 번째 단계를 등록합니다. 앱이 모든 사용자에게 다중 인증(MFA)을 요구한다면 이 방법을 사용하세요.

  • 등록 시 건너뛸 수 있는 옵션으로 두 번째 단계를 등록하는 옵션을 제공하세요. 앱에서 다중 인증(MFA)을 권장하지만 필수가 아니라면 이 방법을 사용할 수 있습니다.

  • 가입 화면이 아닌 사용자의 계정 또는 프로필 관리 페이지에서 두 번째 단계를 추가할 수 있도록 합니다. 이렇게 하면 등록 프로세스 중에 발생하는 마찰을 최소화하면서도 보안에 민감한 사용자에게 다중 인증(MFA)을 제공할 수 있습니다.

  • 사용자가 보안 요구사항이 향상된 기능에 액세스하려고 할 때 두 번째 단계를 점진적으로 추가하도록 합니다.

TOTP MFA에 사용자 등록

앱의 두 번째 단계로 TOTP MFA를 사용 설정한 후 사용자를 TOTP MFA에 등록하도록 클라이언트 측 로직을 구현합니다.

  1. 필요한 MFA 클래스와 함수를 가져옵니다.

    import {
      multiFactor,
      TotpMultiFactorGenerator,
      TotpSecret,
      getAuth,
    } from "firebase/auth";
    
  2. 사용자를 다시 인증합니다.

  3. 인증된 사용자의 TOTP 보안 비밀을 생성합니다.

    // Generate a TOTP secret.
    const multiFactorSession = await multiFactor(currentUser).getSession();
    const totpSecret = await TotpMultiFactorGenerator.generateSecret(
      multiFactorSession
    );
    
  4. 보안 비밀을 사용자에게 표시하고 OTP 앱에 입력하라는 메시지를 표시합니다.

    많은 OTP 앱에서 사용자는 Google OTP 호환 키 URI를 나타내는 QR 코드를 스캔하여 새 TOTP 보안 비밀을 빠르게 추가할 수 있습니다. 이 용도로 QR 코드를 생성하려면 generateQrCodeUrl()로 URI를 생성한 다음 원하는 QR 코드 라이브러리를 사용하여 인코딩합니다. 예를 들면 다음과 같습니다.

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

    QR 코드 표시 여부와 관계없이 항상 보안 비밀 키를 표시하여 QR 코드를 읽을 수 없는 OTP 앱을 지원합니다.

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

    사용자가 OTP 앱에 보안 비밀을 추가하면 TOTP가 생성되기 시작합니다.

  5. 사용자에게 OTP 앱에 표시된 TOTP를 입력하고 이를 사용하여 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);
    

두 번째 단계 인증으로 사용자 로그인 처리

TOTP MFA로 사용자 로그인을 처리하려면 다음 코드를 사용하세요.

  1. 필요한 MFA 클래스와 함수를 가져옵니다.

    import {
        getAuth,
        getMultiFactorResolver,
        TotpMultiFactorGenerator,
    } from "firebase/auth";
    
  2. MFA를 사용하지 않을 때와 마찬가지로 signInWith- 메서드 중 하나를 호출합니다 (예: signInWithEmailAndPassword()). 메서드에서 auth/multi-factor-auth-required 오류가 발생하면 앱의 MFA 흐름을 시작합니다.

    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. 앱의 MFA 흐름에서 먼저 사용자에게 사용하려는 두 번째 단계를 선택하라는 메시지를 표시해야 합니다. MultiFactorResolver 인스턴스의 hints 속성을 검사하여 지원되는 두 번째 단계 목록을 가져올 수 있습니다.

    const mfaResolver = getMultiFactorResolver(getAuth(), error);
    const enrolledFactors = mfaResolver.hints.map(info => info.displayName);
    
  4. 사용자가 TOTP 사용을 선택하면 OTP 앱에 표시된 TOTP를 입력하고 이를 사용하여 로그인하라는 메시지를 표시합니다.

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

TOTP MFA 등록 해제

이 섹션에서는 TOTP MFA에서 등록 해제하는 사용자를 처리하는 방법을 설명합니다.

사용자가 여러 MFA 옵션에 가입했고, 가장 최근에 사용 설정된 옵션에서 등록 해제한 경우 auth/user-token-expired를 수신하고 로그아웃됩니다. 사용자는 다시 로그인하여 기존 사용자 인증 정보(예: 이메일 주소와 비밀번호)를 확인합니다.

사용자를 등록 해제하고 오류를 처리하고 재인증을 트리거하려면 다음 코드를 사용하세요.

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

다음 단계