Fix fingerprint: adaptive auth screen (login/register by state), auto-prompt biometric on open, optimistic switch, real error messages
This commit is contained in:
@@ -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 ----------
|
||||
|
||||
Reference in New Issue
Block a user