Skip to content

App Lock Security

DWallet includes comprehensive security features to protect user financial data through PIN-based authentication, biometric support, and intelligent auto-lock mechanisms.

  • PIN Authentication: 4-digit PIN for quick access
  • Biometric Support: Fingerprint and Face ID (device dependent)
  • Auto-Lock: Automatic lock after app goes to background
  • Grace Period: 30-second grace period for quick app switching
  • Lockout Protection: 5 failed attempts triggers 30-second lockout
  • Shake Animation: Visual feedback on incorrect PIN

File: lib/app/providers/security_provider.dart

Central state management for security features:

class SecurityNotifier extends Notifier<SecurityState> {
static const int _gracePeriodSeconds = 30;
@override
SecurityState build() {
final storage = ref.read(securityStorageServiceProvider);
// Load saved state from storage
return SecurityState(
isAppLockEnabled: storage.isAppLockEnabled(),
isBiometricsEnabled: storage.isBiometricsEnabled(),
hasPin: storage.getPin() != null,
isLocked: shouldLockOnStart,
);
}
Future<void> setPin(String pin) async {
// Save PIN and enable app lock
}
bool verifyPin(String enteredPin) {
// Verify entered PIN against stored PIN
}
void lock() {
// Lock the app immediately
}
void unlock() {
// Unlock the app
}
void onAppBackground() {
// Called when app goes to background
}
bool onAppForeground() {
// Called when app returns to foreground
// Returns true if app should be locked
}
}

File: lib/app/providers/lockout_provider.dart

Manages failed attempt tracking:

class LockoutNotifier extends Notifier<LockoutState> {
static const int maxAttempts = 5;
static const int lockoutDurationSeconds = 30;
void recordFailedAttempt() {
// Increment failed attempts
// Start lockout if max reached
}
void reset() {
// Clear failed attempts
}
}

File: lib/app/core/_app_lifecycle_observer.dart

Watches app lifecycle for background/foreground transitions:

class AppLockLifecycleObserver extends ConsumerStatefulWidget {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
final notifier = ref.read(securityProvider.notifier);
switch (state) {
case AppLifecycleState.paused:
case AppLifecycleState.inactive:
notifier.onAppBackground();
break;
case AppLifecycleState.resumed:
notifier.onAppForeground();
break;
}
}
}

File: lib/app/widgets/_app_lock_screen.dart

The lock screen features:

  • Large PIN indicators
  • Shake animation on error
  • Biometric button (if enabled)
  • Lockout countdown display
class AppLockScreen extends ConsumerStatefulWidget {
// Full-screen overlay with:
// - Lock icon
// - PIN indicators (4 dots)
// - Numeric keypad
// - Biometric button
// - Error messages
}

File: lib/main.dart

The lock screen is shown as an overlay when needed:

class DWalletApp extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final showLockScreen = ref.watch(showLockScreenProvider);
return ShadApp.router(
builder: (context, child) {
return Stack(
children: [
child!,
if (showLockScreen) const AppLockScreen(), // Overlay
],
);
},
);
}
}

File: lib/app/pages/client/settings/pages/app_lock_security/app_lock_security_view.dart

Settings page for configuring security:

  • Toggle app lock on/off
  • Set/change PIN
  • Enable/disable biometrics
  • View security status
1. User navigates to Settings > App Lock Security
2. Taps "Set PIN"
3. Enters 4-digit PIN
4. Confirms PIN
5. PIN saved to secure storage
6. App lock enabled automatically
1. App starts or returns from background (>30s)
2. Lock screen overlay appears
3. User enters PIN or uses biometrics
4. If correct: App unlocks
5. If incorrect: Failed attempt recorded
6. After 5 failures: 30-second lockout
App goes to background
Timestamp saved
App returns to foreground
Check elapsed time
< 30 seconds: No lock
> 30 seconds: Show lock screen

Security data is stored using SecurityStorageService:

class SecurityStorageService {
Future<void> savePin(String pin) async;
String? getPin();
Future<void> setAppLockEnabled(bool enabled) async;
bool isAppLockEnabled();
Future<void> setBiometricsEnabled(bool enabled) async;
bool isBiometricsEnabled();
}

Note: PIN is stored as plaintext in SharedPreferences. For production apps requiring higher security, consider using flutter_secure_storage with encryption.

Uses local_auth package:

class BiometricService {
Future<bool> authenticate() async {
return await _localAuth.authenticate(
localizedReason: 'Please authenticate to access the app',
biometricOnly: true,
);
}
Future<bool> canCheckBiometrics() async {
return await _localAuth.canCheckBiometrics &&
await _localAuth.isDeviceSupported();
}
}

File: android/app/src/main/kotlin/.../MainActivity.kt

import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity : FlutterFragmentActivity() // Required for local_auth

File: android/app/src/main/AndroidManifest.xml

<application
android:enableOnBackInvokedCallback="true"
...>

Edit SecurityNotifier:

static const int _gracePeriodSeconds = 60; // Change to 1 minute

Edit LockoutNotifier:

static const int maxAttempts = 3; // Stricter policy
static const int lockoutDurationSeconds = 60; // Longer lockout

Extend AppLockScreen or modify _app_lock_screen.dart:

class CustomLockScreen extends AppLockScreen {
@override
Widget build(BuildContext context) {
// Your custom UI
}
}
  1. Always lock on fresh start: If app lock is enabled, lock immediately
  2. Respect grace period: Don’t lock if user quickly switches apps
  3. Clear sensitive data: Clear PIN input on each attempt
  4. Visual feedback: Show shake animation on incorrect PIN
  5. Biometric fallback: Always offer PIN as fallback to biometrics
  6. Secure storage: Use encrypted storage for production apps
  7. Rate limiting: Implement lockout after failed attempts
  1. Check device supports biometrics
  2. Ensure biometrics are enrolled in device settings
  3. Verify FlutterFragmentActivity is used
  4. Check Android manifest has proper permissions
  1. Verify AppLockLifecycleObserver wraps the app
  2. Check showLockScreenProvider logic
  3. Ensure isAppLockEnabled is true
  4. Verify PIN has been set