Flutter 앱에 다중 인증(MFA) 추가

Identity Platform을 사용하여 Firebase 인증으로 업그레이드했다면 Flutter 앱에 SMS 다중 인증(MFA)을 추가할 수 있습니다.

다중 인증(MFA)을 통해 앱의 보안이 강화됩니다. 공격자는 종종 비밀번호와 소셜 미디어 계정을 유출시키지만 문자 메시지를 가로채는 것은 더 어렵습니다.

시작하기 전에

  1. 다중 인증(MFA)을 지원하는 제공업체를 하나 이상 사용 설정합니다. 전화 인증, 익명 인증, Apple Game Center를 제외한 모든 제공업체는 MFA를 지원합니다.

  2. 앱이 사용자 이메일을 확인하고 있는지 확인합니다. MFA를 사용하려면 이메일 인증이 필요합니다. 이를 통해 악의적인 행위자가 자신이 소유하지 않은 이메일에 서비스를 등록한 후 두 번째 단계를 추가하여 실제 소유자의 접근을 막는 일을 방지할 수 있습니다.

  3. Android: Firebase Console에서 앱의 SHA-256 해시를 아직 설정하지 않았다면 지금 설정합니다. 앱의 SHA-256 해시를 찾는 방법은 클라이언트 인증을 참조하세요.

  4. iOS: Xcode에서 프로젝트에 푸시 알림을 사용 설정하고 APN 인증 키가 Firebase 클라우드 메시징(FCM)으로 구성되었는지 확인합니다. 이 단계에 대한 자세한 설명은 Firebase iOS 전화 인증 문서를 참조하세요.

  5. : Firebase ConsoleOAuth 리디렉션 도메인에서 애플리케이션 도메인을 추가했는지 확인합니다.

다중 인증(MFA) 사용 설정

  1. Firebase Console의 인증 > 로그인 방법 페이지를 엽니다.

  2. 고급 섹션에서 SMS 다중 인증(MFA)을 사용 설정합니다.

    앱을 테스트할 전화번호도 입력해야 합니다. 선택사항이지만 개발 중 제한이 발생하지 않도록 테스트 전화번호를 등록하는 것이 좋습니다.

  3. 아직 앱 도메인을 승인하지 않았다면 Firebase Console의 인증 > 설정 페이지에서 허용 목록에 추가합니다.

등록 패턴 선택

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

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

  • 등록 시 건너뛸 수 있는 옵션으로 두 번째 단계를 등록하는 옵션을 제공하세요. 다중 인증(MFA)을 권고하지만 필수이지는 않은 앱은 이 방법을 사용하는 것이 좋습니다.

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

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

두 번째 단계 등록

사용자의 새로운 두 번째 단계를 등록하려면 다음 단계를 따르세요.

  1. 사용자를 다시 인증합니다.

  2. 사용자에게 전화번호를 입력하도록 요청합니다.

  3. 사용자를 위한 다중 세션을 가져옵니다.

    final multiFactorSession = await user.multiFactor.getSession();
    
  4. 다중 인증 세션 및 콜백으로 전화번호를 확인합니다.

    await FirebaseAuth.instance.verifyPhoneNumber(
      multiFactorSession: multiFactorSession,
      phoneNumber: phoneNumber,
      verificationCompleted: (_) {},
      verificationFailed: (_) {},
      codeSent: (String verificationId, int? resendToken) async {
        // The SMS verification code has been sent to the provided phone number.
        // ...
      },
      codeAutoRetrievalTimeout: (_) {},
    ); 
    
  5. SMS 코드가 전송되면 사용자에게 코드를 확인하도록 요청합니다.

    final credential = PhoneAuthProvider.credential(
      verificationId: verificationId,
      smsCode: smsCode,
    );
    
  6. 등록을 완료합니다.

    await user.multiFactor.enroll(
      PhoneMultiFactorGenerator.getAssertion(
        credential,
      ),
    );
    

다음 코드는 두 번째 단계를 등록하는 전체 예시를 보여줍니다.

  final session = await user.multiFactor.getSession();
  final auth = FirebaseAuth.instance;
  await auth.verifyPhoneNumber(
    multiFactorSession: session,
    phoneNumber: phoneController.text,
    verificationCompleted: (_) {},
    verificationFailed: (_) {},
    codeSent: (String verificationId, int? resendToken) async {
      // See `firebase_auth` example app for a method of retrieving user's sms code: 
      // https://github.com/firebase/flutterfire/blob/master/packages/firebase_auth/firebase_auth/example/lib/auth.dart#L591
      final smsCode = await getSmsCodeFromUser(context);

      if (smsCode != null) {
        // Create a PhoneAuthCredential with the code
        final credential = PhoneAuthProvider.credential(
          verificationId: verificationId,
          smsCode: smsCode,
        );

        try {
          await user.multiFactor.enroll(
            PhoneMultiFactorGenerator.getAssertion(
              credential,
            ),
          );
        } on FirebaseAuthException catch (e) {
          print(e.message);
        }
      }
    },
    codeAutoRetrievalTimeout: (_) {},
  );

수고하셨습니다. 사용자의 두 번째 인증 단계가 성공적으로 등록되었습니다.

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

2단계 SMS 인증으로 사용자를 로그인 처리하려면 다음 안내를 따르세요.

  1. 첫 번째 단계로 사용자를 로그인한 다음 FirebaseAuthMultiFactorException 예외를 포착합니다. 이 오류에는 사용자의 등록된 두 번째 단계를 가져오는 데 사용할 수 있는 리졸버가 포함됩니다. 첫 번째 단계로 사용자를 성공적으로 인증했음을 나타내는 기본 세션도 포함됩니다.

    예를 들어 사용자의 첫 번째 단계가 이메일과 비밀번호인 경우는 다음과 같습니다.

    try {
      await _auth.signInWithEmailAndPassword(
          email: emailController.text,
          password: passwordController.text,
      );
      // User is not enrolled with a second factor and is successfully
      // signed in.
      // ...
    } on FirebaseAuthMultiFactorException catch (e) {
      // The user is a multi-factor user. Second factor challenge is required
      final resolver = e.resolver
      // ...
    }
    
  2. 사용자가 보조 단계를 여러 개 등록한 경우 어떤 단계를 사용할지 질문합니다.

    final session = e.resolver.session;
    
    final hint = e.resolver.hints[selectedHint];
    
  3. 힌트와 다중 인증 세션으로 사용자의 휴대전화에 인증 메시지를 보냅니다.

    await FirebaseAuth.instance.verifyPhoneNumber(
      multiFactorSession: session,
      multiFactorInfo: hint,
      verificationCompleted: (_) {},
      verificationFailed: (_) {},
      codeSent: (String verificationId, int? resendToken) async {
        // ...
      },
      codeAutoRetrievalTimeout: (_) {},
    );
    
  4. resolver.resolveSignIn()을 호출하여 2차 인증을 완료합니다.

    final smsCode = await getSmsCodeFromUser(context);
    if (smsCode != null) {
      // Create a PhoneAuthCredential with the code
      final credential = PhoneAuthProvider.credential(
        verificationId: verificationId,
        smsCode: smsCode,
      );
    
      try {
        await e.resolver.resolveSignIn(
          PhoneMultiFactorGenerator.getAssertion(credential)
        );
      } on FirebaseAuthException catch (e) {
        print(e.message);
      }
    }
    

아래 코드는 다중 인증 사용자를 위한 전체 로그인 예시를 보여줍니다.

try {
  await _auth.signInWithEmailAndPassword(
    email: emailController.text,
    password: passwordController.text,
  );
} on FirebaseAuthMultiFactorException catch (e) {
  setState(() {
    error = '${e.message}';
  });
  final firstHint = e.resolver.hints.first;
  if (firstHint is! PhoneMultiFactorInfo) {
    return;
  }
  await FirebaseAuth.instance.verifyPhoneNumber(
    multiFactorSession: e.resolver.session,
    multiFactorInfo: firstHint,
    verificationCompleted: (_) {},
    verificationFailed: (_) {},
    codeSent: (String verificationId, int? resendToken) async {
      // See `firebase_auth` example app for a method of retrieving user's sms code: 
      // https://github.com/firebase/flutterfire/blob/master/packages/firebase_auth/firebase_auth/example/lib/auth.dart#L591
      final smsCode = await getSmsCodeFromUser(context);

      if (smsCode != null) {
        // Create a PhoneAuthCredential with the code
        final credential = PhoneAuthProvider.credential(
          verificationId: verificationId,
          smsCode: smsCode,
        );

        try {
          await e.resolver.resolveSignIn(
            PhoneMultiFactorGenerator.getAssertion(
              credential,
            ),
          );
        } on FirebaseAuthException catch (e) {
          print(e.message);
        }
      }
    },
    codeAutoRetrievalTimeout: (_) {},
  );
} catch (e) {
  ...
} 

수고하셨습니다. 다중 인증(MFA)을 사용하여 사용자가 성공적으로 로그인했습니다.

다음 단계