Fix fingerprint: adaptive auth screen (login/register by state), auto-prompt biometric on open, optimistic switch, real error messages

This commit is contained in:
cania
2026-08-13 09:52:03 +02:00
parent 0fd595d8a6
commit 6a9c4da0c7
3 changed files with 108 additions and 58 deletions

View File

@@ -1,3 +1,6 @@
import 'dart:io';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:local_auth/local_auth.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -50,39 +53,41 @@ class AuthService {
}
}
/// Prompt the OS biometric dialog. Returns true on success.
/// [errorOut] receives a human-readable reason when it fails.
Future<bool> authenticateWithBiometrics({String? errorOut}) async {
try {
// Prefer strict biometric (fingerprint). If the device only has
// weaker biometrics (face) or none enrolled, fall back to allowing
// device credentials so the user isn't hard-blocked.
bool ok = false;
/// Prompt the OS biometric dialog.
/// Returns (success, errorMessage). errorMessage is null on success.
Future<(bool, String?)> authenticateWithBiometrics() async {
String? lastErr;
// Prefer strict biometric (fingerprint). If that fails, fall back to
// device credentials (PIN/pattern) so the user isn't hard-blocked.
for (final opt in [
const AuthenticationOptions(
biometricOnly: true, stickyAuth: true, sensitiveTransaction: true),
const AuthenticationOptions(
biometricOnly: false, stickyAuth: true, sensitiveTransaction: true),
]) {
try {
ok = await _localAuth.authenticate(
final ok = await _localAuth.authenticate(
localizedReason: 'Gunakan biometrik untuk membuka Login Vault',
options: const AuthenticationOptions(
biometricOnly: true,
stickyAuth: true,
),
options: opt,
);
} catch (_) {
ok = false;
if (ok) return (true, null);
} on PlatformException catch (e) {
lastErr = '${e.code}: ${e.message}';
// user cancel / negative button -> stop retrying
if (e.code == 'PasscodeNotSet' ||
e.code == 'NotAvailable' ||
e.code == 'NotEnrolled' ||
e.code == 'LockedOut' ||
e.code == 'UserCancel' ||
e.code == 'Canceled' ||
e.code == 'UserFallback') {
return (false, lastErr);
}
} catch (e) {
lastErr = e.toString();
}
if (!ok) {
ok = await _localAuth.authenticate(
localizedReason: 'Gunakan biometrik / kredensial perangkat',
options: const AuthenticationOptions(
biometricOnly: false,
stickyAuth: true,
),
);
}
return ok;
} catch (e) {
if (errorOut != null) errorOut = e.toString();
return false;
}
return (false, lastErr);
}
// ---------- Session ----------