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

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

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

시작하기 전에

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

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

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

    TOTP MFA는 Apple SDK 버전 v10.12.0 이상 및 iOS에서만 지원됩니다.

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. 사용자를 다시 인증합니다.

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

    // 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. 보안 비밀을 사용자에게 표시하고 OTP 앱에 입력하라는 메시지를 표시합니다.

    // Display this key:
    let secret = totpSecret.sharedSecretKey()
    

    보안 비밀 키를 표시하는 것 외에도 기기의 기본 OTP 앱에 보안 비밀 키를 자동으로 추가하도록 시도할 수 있습니다. 이렇게 하려면 Google OTP 호환 키 URI를 생성하여 openInOTPApp(withQRCodeURL:)에 전달합니다.

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

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

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

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

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

  1. MFA를 사용하지 않을 때와 마찬가지로 signIn(with...:) 메서드 중 하나를 호출합니다(예: signIn(withEmail:password:)). 메서드에서 secondFactorRequired 코드와 함께 오류가 발생하면 앱의 MFA 흐름을 시작합니다.

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

    let mfaKey = AuthErrorUserInfoMultiFactorResolverKey
    guard let resolver = error.userInfo[mfaKey] as? MultiFactorResolver else { return }
    let enrolledFactors = resolver.hints.map(\.displayName)
    
  3. 사용자가 TOTP 사용을 선택하면 OTP 앱에 표시된 TOTP를 입력하고 이를 사용하여 로그인하라는 메시지를 표시합니다.

    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
    }
    

TOTP MFA 등록 해제

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

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

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

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

다음 단계