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

Nếu đã nâng cấp lên Firebase Authentication bằng Identity Platform, bạn có thể thêm tính năng xác thực đa yếu tố qua SMS vào ứng dụng Flutter.

Tính năng xác thực đa yếu tố (MFA) giúp tăng cường độ bảo mật cho ứng dụng của bạn. Mặc dù kẻ tấn công thường xâm phạm mật khẩu và tài khoản mạng xã hội, nhưng việc chặn tin nhắn văn bản sẽ khó hơn.

Trước khi bắt đầu

  1. Bật ít nhất một nhà cung cấp hỗ trợ tính năng xác thực đa yếu tố. Mọi nhà cung cấp đều hỗ trợ tính năng xác thực đa yếu tố, ngoại trừ tính năng xác thực bằng điện thoại, xác thực ẩn danh và Apple Game Center.

  2. Đảm bảo ứng dụng của bạn đang xác minh email của người dùng. Tính năng xác thực đa yếu tố yêu cầu xác minh email. Điều này giúp ngăn chặn các tác nhân độc hại đăng ký một dịch vụ bằng email không thuộc sở hữu của họ, sau đó khoá chủ sở hữu thực sự bằng cách thêm yếu tố thứ hai.

Bật tính năng xác thực đa yếu tố

  1. Trong bảng điều khiển Firebase, hãy chuyển đến phần Security > Authentication.

  2. Trong thẻ Sign-in method (Phương thức đăng nhập), ở phần Advanced (Nâng cao), hãy bật SMS Multi-factor Authentication (Xác thực đa yếu tố qua SMS).

    Bạn cũng nên nhập số điện thoại mà bạn sẽ dùng để kiểm thử ứng dụng. Mặc dù không bắt buộc, nhưng bạn nên đăng ký số điện thoại kiểm thử để tránh bị điều tiết trong quá trình phát triển.

  3. Nếu bạn chưa làm, hãy cho phép miền của ứng dụng:

    1. Trong bảng điều khiển Firebase, hãy chuyển đến thẻ Security > Authentication > Settings.

    2. Trong phần Authorized domains (Miền được phép), hãy nhấp vào Add domain (Thêm miền) rồi thêm miền của bạn.

Chọn mẫu đăng ký

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

  • Đăng ký yếu tố thứ hai của người dùng trong quá trình đăng ký. Hãy sử dụng phương thức này nếu ứng dụng của bạn yêu cầu tính năng xác thực đa yếu tố cho tất cả người dùng.

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

  • Cung cấp khả năng thêm yếu tố thứ hai trên trang quản lý tài khoản hoặc hồ sơ của người dùng, thay vì trên màn hình đăng ký. Điều này giúp giảm thiểu sự gián đoạn trong quá trình đăng ký, đồng thời vẫn cung cấp tính năng xác thực đa yếu tố cho những người dùng nhạy cảm về vấn đề 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 vào các tính năng có yêu cầu bảo mật cao hơn.

Đăng ký yếu tố thứ hai

Cách đăng ký yếu tố thứ hai mới cho người dùng:

  1. Xác thực lại người dùng.

  2. Yêu cầu người dùng nhập số điện thoại.

  3. Nhận phiên xác thực đa yếu tố cho người dùng:

    final multiFactorSession = await user.multiFactor.getSession();
    
  4. Xác minh số điện thoại bằng phiên xác thực đa yếu tố và lệnh gọi lại của bạn:

    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. Sau khi gửi mã SMS, hãy yêu cầu người dùng xác minh mã:

    final credential = PhoneAuthProvider.credential(
      verificationId: verificationId,
      smsCode: smsCode,
    );
    
  6. Hoàn tất quá trình đăng ký:

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

Đoạn mã dưới đây cho thấy ví dụ hoàn chỉnh về cách đăng ký yếu tố thứ hai:

  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: (_) {},
  );

Xin chúc mừng! Bạn đã đăng ký thành công yếu tố xác thực thứ hai cho người dùng.

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

Cách đăng nhập cho người dùng bằng tính năng xác minh 2 yếu tố qua SMS:

  1. Đăng nhập cho người dùng bằng yếu tố đầu tiên, sau đó bắt ngoại lệ FirebaseAuthMultiFactorException. Lỗi này chứa một trình phân giải mà bạn có thể dùng để lấy các yếu tố thứ hai đã đăng ký của người dùng. Lỗi này cũng chứa một phiên cơ bản chứng minh người dùng đã xác thực thành công bằng yếu tố đầu tiên.

    Ví dụ: nếu yếu tố đầu tiên của người dùng là email và mật khẩu:

    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. Nếu người dùng đã đăng ký nhiều yếu tố thứ hai, hãy hỏi họ muốn sử dụng yếu tố nào:

    final session = e.resolver.session;
    
    final hint = e.resolver.hints[selectedHint];
    
  3. Gửi thông báo xác minh đến điện thoại của người dùng kèm theo gợi ý và phiên xác thực đa yếu tố:

    await FirebaseAuth.instance.verifyPhoneNumber(
      multiFactorSession: session,
      multiFactorInfo: hint,
      verificationCompleted: (_) {},
      verificationFailed: (_) {},
      codeSent: (String verificationId, int? resendToken) async {
        // ...
      },
      codeAutoRetrievalTimeout: (_) {},
    );
    
  4. Gọi resolver.resolveSignIn() để hoàn tất quá trình xác thực thứ hai:

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

Đoạn mã dưới đây cho thấy ví dụ hoàn chỉnh về cách đăng nhập cho người dùng có nhiều yếu tố:

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

Xin chúc mừng! Bạn đã đăng nhập thành công cho người dùng bằng tính năng xác thực đa yếu tố.

Bước tiếp theo