Flutter - запрашивать TouchID/FaceID при открытии приложения

У меня проблема с реализацией аутентификации TouchID/FaceID таким образом, что она автоматически запрашивает пользователя при открытии приложения. Я использую зависимость local_auth для TouchID/FaceID.

В приведенном ниже коде биометрическая аутентификация появится после возобновления работы приложения, но ее также невозможно отменить. Если вы нажмете кнопку "Домой", она отклонит запрос TouchID, но сразу же начнет повторную попытку и вызовет бесконечный цикл, если вы продолжаете пробовать это. Он также дважды будет выдавать случайный запрос, поэтому, даже если вы успешно войдете в систему с первым запросом TouchID, он сразу же появится снова. Кто-нибудь знает способ исправить это? У меня также есть кнопка TouchID на странице входа, которую пользователи могут нажимать, чтобы вручную запросить TouchID, но я бы хотел воссоздать, как работают мои банковские приложения и другие, когда TouchID запрашивает, когда вы автоматически открываете приложение.

void initState() {
  super.initState();

  SystemChannels.lifecycle.setMessageHandler((msg) async {
    if (msg==AppLifecycleState.resumed.toString()) {
      // If can pop, app is not at login screen
      // so don't prompt for authentication
      if (!Navigator.canPop(context)) {
        if (_useFingerprint) {
          SharedPreferences prefs = await SharedPreferences.getInstance();
          String password = prefs.getString('password');
          biometricAuthentication(_username, password);
        }
     }
  }
});

void biometricAuthentication(String user, String pass) async {
  print("Biometric Authentication Called");
  bool didAuthenticate = false;
  try {
    didAuthenticate = await localAuth.authenticateWithBiometrics(localizedReason: 'Please authenticate');
  } on PlatformException catch (e) {
    print(e);
  }

  if (!mounted) {
    return;
  } 

  if (!didAuthenticate) {
    return;
  }
  else {
    normalLogin(user, pass);   
  }
}

4 ответа

Попробуй это:

Создайте класс для управления жизнью вашего приложения

      class LifecycleEventHandler extends WidgetsBindingObserver {
      final Function onResume;
    
      LifecycleEventHandler(this.onResume);
    
      @override
      Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
        switch (state) {
          case AppLifecycleState.resumed:
            onResume.call();
            break;
          default:
            break;
        }
      }
    }

Затем в вашем основном:

      @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(
      LifecycleEventHandler(() async {
        //Put your auth function here (biometric auth or passcode)
      }),
    );
  }

Каждый раз, когда вы возвращаетесь в приложение, действие, которое вы поставили, будет выполняться

Думаю, мне удалось ее решить. Если есть какие-либо другие проблемы, дайте мне знать.

      Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  bool isAuthenticated = await Authentication.authenticateWithBiometrics();

  if (isAuthenticated) {
   runApp(MyApp());
 } else {
   main();
 }
}

Кажется, это работает каждый раз, когда я открываю приложение. Более того, вот мой файл authentification.dart

      class Authentication {
  static Future<bool> authenticateWithBiometrics() async {
   final LocalAuthentication localAuthentication = LocalAuthentication();
   bool isBiometricSupported = await localAuthentication.isDeviceSupported();
   bool canCheckBiometrics = await localAuthentication.canCheckBiometrics;

   bool isAuthenticated = false;

   if (isBiometricSupported && canCheckBiometrics) {
     isAuthenticated = await localAuthentication.authenticate(
       localizedReason: 'Please complete the biometrics to proceed.',
       //biometricOnly: true,
       stickyAuth: true,
     );
   }

   return isAuthenticated;
 }
}

Это сработало для меня

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  if (await _isBiometricAvailable()) {
    await _getListOfBiometricTypes();
    await _authenticateUser();
  }

  runApp(App());
}

Отказ от ответственности: я не очень разбираюсь в разработке под iOS, поэтому я немного экстраполирую из Android здесь.

Я считаю, что проблема в том, что системные диалоги делают ваше приложение неактивным, что ведет к бесконечному циклу

  1. пользователь возобновляет приложение
  2. приложение отображает диалог TouchID/FaceID, тем самым делая себя неактивным
  3. пользователь подтверждает диалог
  4. диалог закрывается
  5. Ваше приложение снова выходит на передний план и тем самым возобновляется
  6. см шаг 2

Возможные решения

  • Не запрашивайте аутентификацию при запуске приложения, а скорее, когда в приложении происходят важные действия. Именно так должна была использоваться функция аутентификации, поэтому это самое идиоматическое решение. (мое любимое)
  • Установите ограничение по времени, например, показывать диалог только в том случае, если пользователь отсутствовал более x секунд, тем самым отфильтровывая длинные интерактивные фазы от коротких (в том числе для аутентификации). (для меня это обходной путь)
Другие вопросы по тегам