เพิ่มการตรวจสอบสิทธิ์แบบหลายปัจจัยในแอป Flutter

หากคุณอัปเกรดเป็น การตรวจสอบสิทธิ์ Firebase with Identity Platform แล้ว คุณสามารถเพิ่ม การตรวจสอบสิทธิ์แบบหลายปัจจัย ทาง SMS ลงในแอป Flutter ได้

การตรวจสอบสิทธิ์แบบหลายปัจจัย (MFA) ช่วยเพิ่มความปลอดภัยให้แอปของคุณ ผู้โจมตีมักเจาะรหัสผ่านและบัญชีโซเชียล แต่การสกัดกั้นข้อความมีความซับซ้อนมากกว่า

ก่อนเริ่มต้น

  1. เปิดใช้ผู้ให้บริการอย่างน้อย 1 รายที่รองรับการตรวจสอบสิทธิ์แบบหลายปัจจัย ผู้ให้บริการทุกรายรองรับ MFA ยกเว้น การตรวจสอบสิทธิ์ทางโทรศัพท์ การตรวจสอบสิทธิ์แบบไม่ระบุตัวตน และ Apple Game Center

  2. ตรวจสอบว่าแอปของคุณกำลังยืนยันอีเมลของผู้ใช้ เนื่องจาก MFA ต้องมีการยืนยันอีเมล ซึ่งจะช่วยป้องกันไม่ให้ผู้ไม่ประสงค์ดีลงทะเบียนใช้บริการด้วยอีเมลที่ไม่ใช่ของตนเอง แล้วล็อกไม่ให้เจ้าของที่แท้จริงเข้าถึงบัญชีได้โดยการเพิ่มปัจจัยที่ 2

การเปิดใช้การตรวจสอบสิทธิ์แบบหลายปัจจัย

  1. ในคอนโซล Firebase ให้ไปที่ความปลอดภัย > การตรวจสอบสิทธิ์

  2. ในแท็บวิธีการลงชื่อเข้าใช้ ในส่วนขั้นสูง ให้เปิดใช้การตรวจสอบสิทธิ์แบบหลายปัจจัยทาง SMS

    นอกจากนี้ คุณควรป้อนหมายเลขโทรศัพท์ที่จะใช้ทดสอบแอปด้วย แม้ว่าจะเป็นตัวเลือก แต่เราขอแนะนำให้ลงทะเบียนหมายเลขโทรศัพท์ทดสอบเพื่อหลีกเลี่ยงการจำกัดอัตราการใช้งานระหว่างการพัฒนา

  3. อนุญาตโดเมนของแอปหากยังไม่ได้ทำ โดยทำดังนี้

    1. ในคอนโซลFirebase ให้ไปที่แท็บ ความปลอดภัย > การตรวจสอบสิทธิ์ > การตั้งค่า

    2. ในส่วนโดเมนที่ได้รับอนุญาต ให้คลิกเพิ่มโดเมน แล้วเพิ่ม โดเมน

การเลือกลักษณะการลงทะเบียน

คุณสามารถเลือกว่าแอปต้องใช้การตรวจสอบสิทธิ์แบบหลายปัจจัยหรือไม่ รวมถึงวิธีและเวลาที่จะลงทะเบียนผู้ใช้ ลักษณะที่พบบ่อยมีดังนี้

  • ลงทะเบียนปัจจัยที่ 2 ของผู้ใช้เป็นส่วนหนึ่งของการลงทะเบียน ใช้วิธีนี้หากแอปกำหนดให้ผู้ใช้ทุกคนต้องใช้การตรวจสอบสิทธิ์แบบหลายปัจจัย

  • เสนอตัวเลือกที่ข้ามได้เพื่อลงทะเบียนปัจจัยที่ 2 ระหว่างการลงทะเบียน แอปที่ต้องการสนับสนุนแต่ไม่บังคับให้ใช้การตรวจสอบสิทธิ์แบบหลายปัจจัยอาจชอบแนวทางนี้

  • ให้ความสามารถในการเพิ่มปัจจัยที่ 2 จากบัญชีของผู้ใช้หรือหน้าการจัดการโปรไฟล์ แทนที่จะเป็นหน้าจอลงชื่อสมัครใช้ วิธีนี้จะช่วยลดความยุ่งยากระหว่างกระบวนการลงทะเบียน ขณะเดียวกันก็ยังคงให้ผู้ใช้ที่กังวลเรื่องความปลอดภัยใช้การตรวจสอบสิทธิ์แบบหลายปัจจัยได้

  • กำหนดให้เพิ่มปัจจัยที่ 2 ทีละรายการเมื่อผู้ใช้ต้องการเข้าถึงฟีเจอร์ที่มีข้อกำหนดด้านความปลอดภัยที่เข้มงวดขึ้น

การลงทะเบียนปัจจัยที่ 2

วิธีลงทะเบียนปัจจัยที่ 2 ใหม่สำหรับผู้ใช้

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

โค้ดด้านล่างแสดงตัวอย่างที่สมบูรณ์ของการลงทะเบียนปัจจัยที่ 2

  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/main/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 สำหรับผู้ใช้เรียบร้อยแล้ว

การลงชื่อเข้าใช้ของผู้ใช้ด้วยปัจจัยที่ 2

วิธีลงชื่อเข้าใช้ของผู้ใช้ด้วยการยืนยันทาง SMS แบบ 2 ปัจจัย

  1. ลงชื่อเข้าใช้ของผู้ใช้ด้วยปัจจัยแรก แล้วดักจับข้อยกเว้น FirebaseAuthMultiFactorException ข้อผิดพลาดนี้มีรีโซลเวอร์ ซึ่งคุณใช้เพื่อรับปัจจัยที่ 2 ที่ลงทะเบียนของผู้ใช้ได้ นอกจากนี้ยังมีเซสชันพื้นฐานที่แสดงว่าผู้ใช้ตรวจสอบสิทธิ์ด้วยปัจจัยแรกสำเร็จ

    ตัวอย่างเช่น หากปัจจัยแรกของผู้ใช้คืออีเมลและรหัสผ่าน ให้ทำดังนี้

    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. หากผู้ใช้ลงทะเบียนปัจจัยที่ 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/main/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) {
  ...
}

ยินดีด้วย คุณลงชื่อเข้าใช้ของผู้ใช้โดยใช้การตรวจสอบสิทธิ์แบบหลายปัจจัยเรียบร้อยแล้ว

ขั้นตอนถัดไป