What changed, and why it matters
This commit adds an optional app-lock feature to the Skylight Wallet app. When enabled, the user must authenticate with their device's biometric or PIN before accessing the wallet. It is a security improvement, not a vulnerability fix or a new security flaw.
No security action required. This is a defensive feature addition. Reviewers may optionally verify that local_auth is configured with strong biometric options and that the lock cannot be trivially bypassed on rooted devices.
Security signals we found
Adds biometric/PIN app lock feature
Uses local_auth plugin for device authentication
Declares Android biometric permissions
Persists lock setting in SharedPreferences
No vulnerability or exploit code present
Evidence from the diff
The change introduces the local_auth Flutter plugin, adds an UnlockScreen that calls LocalAuthentication.authenticate(), and adds a settings toggle that persists appLockEnabled in SharedPreferences. Android permissions USE_BIOMETRIC and USE_FINGERPRINT are declared, and MainActivity is changed to FlutterFragmentActivity as required by local_auth. The app now routes to /unlock instead of /wallet_home when a wallet exists and app lock is enabled. Wallet deletion also clears the appLockEnabled preference.
Changed components
lib/screens/unlock.dartlib/screens/settings.dartlib/main.dartlib/services/shared_preferences_service.dartlib/models/wallet_model.dartandroid/app/src/main/AndroidManifest.xmlandroid/app/src/main/kotlin/com/example/monero_light_wallet/MainActivity.ktpubspec.yamlInspect captured patch +307 / −17
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 58a2220..6ead506 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -2,6 +2,8 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions"/>
+ <uses-permission android:name="android.permission.USE_BIOMETRIC" />
+ <uses-permission android:name="android.permission.USE_FINGERPRINT" />
<application
android:label="monero_light_wallet"
@@ -9,7 +11,6 @@
android:icon="@mipmap/ic_launcher"
android:allowBackup="false">
-
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="incoming_transactions"/>
diff --git a/android/app/src/main/kotlin/com/example/monero_light_wallet/MainActivity.kt b/android/app/src/main/kotlin/com/example/monero_light_wallet/MainActivity.kt
index 47ed5f3..3528398 100644
--- a/android/app/src/main/kotlin/com/example/monero_light_wallet/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/example/monero_light_wallet/MainActivity.kt
@@ -1,5 +1,5 @@
package com.example.monero_light_wallet
-import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.android.FlutterFragmentActivity
-class MainActivity : FlutterActivity()
+class MainActivity : FlutterFragmentActivity()
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index ab8ff35..a735e99 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -45,6 +45,9 @@
"navigationBarWallet": "Wallet",
"navigationBarSettings": "Settings",
"navigationBarKeys": "Keys",
+ "unlockButton": "Unlock",
+ "unlockReason": "Unlock wallet",
+ "unlockUnableToAuthError": "Unable to authenticate.",
"homeConnecting": "Connecting",
"homeSyncing": "Syncing",
"homeHeight": "Height",
@@ -69,7 +72,10 @@
"sendInsufficientBalanceError": "Insufficient balance.",
"sendInsufficientBalanceToCoverFeeError": "Insufficient balance to cover the network fee.",
"settingsTitle": "Settings",
- "settingsNotifyNewTxs": "Notify New Transactions",
+ "settingsNotifyNewTxsLabel": "Notify New Transactions",
+ "settingsAppLockLabel": "App Lock",
+ "settingsAppLockUnlockReason": "Unlock wallet",
+ "settingsAppLockUnableToAuthError": "Unable to authenticate.",
"settingsLanguageLabel": "Language",
"settingsDisplayCurrencyLabel": "Display Currency",
"settingsLwsViewKeysLabel": "LWS View Keys",
diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart
index cb86a62..a7d1eb8 100644
--- a/lib/l10n/app_localizations.dart
+++ b/lib/l10n/app_localizations.dart
@@ -368,6 +368,24 @@ abstract class AppLocalizations {
/// **'Keys'**
String get navigationBarKeys;
+ /// No description provided for @unlockButton.
+ ///
+ /// In en, this message translates to:
+ /// **'Unlock'**
+ String get unlockButton;
+
+ /// No description provided for @unlockReason.
+ ///
+ /// In en, this message translates to:
+ /// **'Unlock wallet'**
+ String get unlockReason;
+
+ /// No description provided for @unlockUnableToAuthError.
+ ///
+ /// In en, this message translates to:
+ /// **'Unable to authenticate.'**
+ String get unlockUnableToAuthError;
+
/// No description provided for @homeConnecting.
///
/// In en, this message translates to:
@@ -512,11 +530,29 @@ abstract class AppLocalizations {
/// **'Settings'**
String get settingsTitle;
- /// No description provided for @settingsNotifyNewTxs.
+ /// No description provided for @settingsNotifyNewTxsLabel.
///
/// In en, this message translates to:
/// **'Notify New Transactions'**
- String get settingsNotifyNewTxs;
+ String get settingsNotifyNewTxsLabel;
+
+ /// No description provided for @settingsAppLockLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'App Lock'**
+ String get settingsAppLockLabel;
+
+ /// No description provided for @settingsAppLockUnlockReason.
+ ///
+ /// In en, this message translates to:
+ /// **'Unlock wallet'**
+ String get settingsAppLockUnlockReason;
+
+ /// No description provided for @settingsAppLockUnableToAuthError.
+ ///
+ /// In en, this message translates to:
+ /// **'Unable to authenticate.'**
+ String get settingsAppLockUnableToAuthError;
/// No description provided for @settingsLanguageLabel.
///
diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart
index 96ff5e3..9f3f042 100644
--- a/lib/l10n/app_localizations_en.dart
+++ b/lib/l10n/app_localizations_en.dart
@@ -151,6 +151,15 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get navigationBarKeys => 'Keys';
+ @override
+ String get unlockButton => 'Unlock';
+
+ @override
+ String get unlockReason => 'Unlock wallet';
+
+ @override
+ String get unlockUnableToAuthError => 'Unable to authenticate.';
+
@override
String get homeConnecting => 'Connecting';
@@ -228,7 +237,16 @@ class AppLocalizationsEn extends AppLocalizations {
String get settingsTitle => 'Settings';
@override
- String get settingsNotifyNewTxs => 'Notify New Transactions';
+ String get settingsNotifyNewTxsLabel => 'Notify New Transactions';
+
+ @override
+ String get settingsAppLockLabel => 'App Lock';
+
+ @override
+ String get settingsAppLockUnlockReason => 'Unlock wallet';
+
+ @override
+ String get settingsAppLockUnableToAuthError => 'Unable to authenticate.';
@override
String get settingsLanguageLabel => 'Language';
diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart
index 66e7fa4..f11e1cc 100644
--- a/lib/l10n/app_localizations_pt.dart
+++ b/lib/l10n/app_localizations_pt.dart
@@ -151,6 +151,15 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get navigationBarKeys => 'Chaves';
+ @override
+ String get unlockButton => 'Desbloquear';
+
+ @override
+ String get unlockReason => 'Desbloquear carteira';
+
+ @override
+ String get unlockUnableToAuthError => 'Não foi possível autenticar.';
+
@override
String get homeConnecting => 'Conectando';
@@ -228,7 +237,16 @@ class AppLocalizationsPt extends AppLocalizations {
String get settingsTitle => 'Configurações';
@override
- String get settingsNotifyNewTxs => 'Notificar Novas Transações';
+ String get settingsNotifyNewTxsLabel => 'Notificar Novas Transações';
+
+ @override
+ String get settingsAppLockLabel => 'Desbloqueio com PIN/Biometria';
+
+ @override
+ String get settingsAppLockUnlockReason => 'Desbloquear carteira';
+
+ @override
+ String get settingsAppLockUnableToAuthError => 'Não foi possível autenticar.';
@override
String get settingsLanguageLabel => 'Idioma';
diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb
index e4ca588..f25377a 100644
--- a/lib/l10n/app_pt.arb
+++ b/lib/l10n/app_pt.arb
@@ -45,6 +45,9 @@
"navigationBarWallet": "Carteira",
"navigationBarSettings": "Configurações",
"navigationBarKeys": "Chaves",
+ "unlockButton": "Desbloquear",
+ "unlockReason": "Desbloquear carteira",
+ "unlockUnableToAuthError": "Não foi possível autenticar.",
"homeConnecting": "Conectando",
"homeSyncing": "Sincronizando",
"homeHeight": "Bloco",
@@ -69,7 +72,10 @@
"sendInsufficientBalanceError": "Saldo insuficiente.",
"sendInsufficientBalanceToCoverFeeError": "Saldo insuficiente para cobrir a taxa da rede.",
"settingsTitle": "Configurações",
- "settingsNotifyNewTxs": "Notificar Novas Transações",
+ "settingsNotifyNewTxsLabel": "Notificar Novas Transações",
+ "settingsAppLockLabel": "Desbloqueio com PIN/Biometria",
+ "settingsAppLockUnlockReason": "Desbloquear carteira",
+ "settingsAppLockUnableToAuthError": "Não foi possível autenticar.",
"settingsLanguageLabel": "Idioma",
"settingsDisplayCurrencyLabel": "Moeda Local",
"settingsLwsViewKeysLabel": "Chaves de Visualização do LWS",
diff --git a/lib/main.dart b/lib/main.dart
index e7a8426..4cd6be9 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:monero_light_wallet/periodic_tasks.dart';
+import 'package:monero_light_wallet/screens/unlock.dart';
+import 'package:monero_light_wallet/services/shared_preferences_service.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:timeago/timeago.dart' as timeago;
@@ -73,8 +75,22 @@ class MyApp extends StatelessWidget {
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
+ final sharedPreferences =
+ snapshot.data![0] as SharedPreferences;
final walletExists = snapshot.data![1] as bool;
- final initialRoute = walletExists ? '/wallet_home' : '/welcome';
+
+ final appLockEnabled =
+ sharedPreferences.getBool(
+ SharedPreferencesKeys.appLockEnabled,
+ ) ??
+ false;
+
+ final initialRoute = walletExists
+ ? appLockEnabled
+ ? '/unlock'
+ : '/wallet_home'
+ : '/welcome';
+
TorService.sharedInstance.start();
if (walletExists) {
@@ -105,6 +121,7 @@ class MyApp extends StatelessWidget {
'/lws_details': (context) => LwsDetailsScreen(),
'/restore_warning': (context) => RestoreWarningScreen(),
'/restore_wallet': (context) => RestoreWalletScreen(),
+ '/unlock': (context) => UnlockScreen(),
'/wallet_home': (context) => WalletHomeScreen(),
'/settings': (context) => SettingsScreen(),
'/lws_keys': (context) => LwsKeysScreen(),
diff --git a/lib/models/wallet_model.dart b/lib/models/wallet_model.dart
index 158ac79..59c6652 100644
--- a/lib/models/wallet_model.dart
+++ b/lib/models/wallet_model.dart
@@ -598,6 +598,7 @@ class WalletModel with ChangeNotifier {
await SharedPreferencesService.remove(
SharedPreferencesKeys.walletRestoreHeight,
);
+ await SharedPreferencesService.remove(SharedPreferencesKeys.appLockEnabled);
}
Future<bool> hasExistingWallet() async {
diff --git a/lib/screens/send.dart b/lib/screens/send.dart
index a9b5325..93cd539 100644
--- a/lib/screens/send.dart
+++ b/lib/screens/send.dart
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:provider/provider.dart';
import 'package:monero_light_wallet/l10n/app_localizations.dart';
import 'package:monero_light_wallet/screens/confirm_send.dart';
import 'package:monero_light_wallet/widgets/monero_amount.dart';
-import 'package:provider/provider.dart';
import 'package:monero_light_wallet/models/wallet_model.dart';
class SendScreenArgs {
diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart
index b06beab..4358ae1 100644
--- a/lib/screens/settings.dart
+++ b/lib/screens/settings.dart
@@ -1,4 +1,8 @@
import 'package:flutter/material.dart';
+import 'package:monero_light_wallet/util/logging.dart';
+import 'package:provider/provider.dart';
+import 'package:local_auth/local_auth.dart';
+
import 'package:monero_light_wallet/consts.dart';
import 'package:monero_light_wallet/l10n/app_localizations.dart';
import 'package:monero_light_wallet/models/fiat_rate_model.dart';
@@ -8,7 +12,6 @@ import 'package:monero_light_wallet/periodic_tasks.dart';
import 'package:monero_light_wallet/services/notifications_service.dart';
import 'package:monero_light_wallet/services/shared_preferences_service.dart';
import 'package:monero_light_wallet/widgets/wallet_navigation_bar.dart';
-import 'package:provider/provider.dart';
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@@ -20,6 +23,7 @@ class SettingsScreen extends StatefulWidget {
class _SettingsScreenState extends State<SettingsScreen> {
var _newTxNotificationsEnabled = false;
var _fiatCurrency = 'USD';
+ var _appLockEnabled = false;
@override
void initState() {
@@ -40,13 +44,20 @@ class _SettingsScreenState extends State<SettingsScreen> {
) ??
'USD';
+ final appLockEnabled =
+ await SharedPreferencesService.get<bool>(
+ SharedPreferencesKeys.appLockEnabled,
+ ) ??
+ false;
+
setState(() {
_newTxNotificationsEnabled = newTxNotificationsEnabled;
_fiatCurrency = fiatCurrency;
+ _appLockEnabled = appLockEnabled;
});
}
- void _setNewTxNotifications(bool value) async {
+ void _setTxNotificationsEnabled(bool value) async {
setState(() {
_newTxNotificationsEnabled = value;
});
@@ -70,6 +81,51 @@ class _SettingsScreenState extends State<SettingsScreen> {
}
}
+ void _setAppLockEnabled(bool value) async {
+ final i18n = AppLocalizations.of(context)!;
+
+ if (value) {
+ final auth = LocalAuthentication();
+
+ try {
+ final didAuthenticate = await auth.authenticate(
+ localizedReason: i18n.settingsAppLockUnlockReason,
+ options: AuthenticationOptions(
+ useErrorDialogs: true,
+ sensitiveTransaction: true,
+ ),
+ );
+
+ if (!didAuthenticate) {
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(i18n.settingsAppLockUnableToAuthError)),
+ );
+ }
+ return;
+ }
+ } catch (error) {
+ log(LogLevel.error, 'Unable to authenticate: ${error.toString()}');
+
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(i18n.settingsAppLockUnableToAuthError)),
+ );
+ }
+ return;
+ }
+ }
+
+ setState(() {
+ _appLockEnabled = value;
+ });
+
+ await SharedPreferencesService.set<bool>(
+ SharedPreferencesKeys.appLockEnabled,
+ value,
+ );
+ }
+
void _setFiatCurrency(String? value) async {
if (value == null) return;
@@ -224,13 +280,23 @@ class _SettingsScreenState extends State<SettingsScreen> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Text(i18n.settingsNotifyNewTxs, style: TextStyle(fontSize: 18)),
+ Text(
+ i18n.settingsNotifyNewTxsLabel,
+ style: TextStyle(fontSize: 18),
+ ),
Switch(
value: _newTxNotificationsEnabled,
- onChanged: _setNewTxNotifications,
+ onChanged: _setTxNotificationsEnabled,
),
],
),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(i18n.settingsAppLockLabel, style: TextStyle(fontSize: 18)),
+ Switch(value: _appLockEnabled, onChanged: _setAppLockEnabled),
+ ],
+ ),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
diff --git a/lib/screens/unlock.dart b/lib/screens/unlock.dart
new file mode 100644
index 0000000..1bf154a
--- /dev/null
+++ b/lib/screens/unlock.dart
@@ -0,0 +1,65 @@
+import 'package:flutter/material.dart';
+import 'package:local_auth/local_auth.dart';
+
+import 'package:monero_light_wallet/l10n/app_localizations.dart';
+import 'package:monero_light_wallet/util/logging.dart';
+
+class UnlockScreen extends StatefulWidget {
+ const UnlockScreen({super.key});
+
+ @override
+ State<UnlockScreen> createState() => _UnlockScreenState();
+}
+
+class _UnlockScreenState extends State<UnlockScreen> {
+ @override
+ void initState() {
+ super.initState();
+ _promptUnlock();
+ }
+
+ Future<void> _promptUnlock() async {
+ final auth = LocalAuthentication();
+
+ try {
+ final didAuthenticate = await auth.authenticate(
+ localizedReason: 'Unlock wallet',
+ options: AuthenticationOptions(
+ useErrorDialogs: true,
+ sensitiveTransaction: true,
+ ),
+ );
+
+ if (didAuthenticate) {
+ if (mounted) Navigator.pushReplacementNamed(context, '/wallet_home');
+ }
+ } catch (error) {
+ log(LogLevel.error, 'Unable to authenticate: ${error.toString()}');
+
+ if (mounted) {
+ ScaffoldMessenger.of(
+ context,
+ ).showSnackBar(SnackBar(content: Text('Unable to authenticate.')));
+ }
+ return;
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final i18n = AppLocalizations.of(context)!;
+
+ return Scaffold(
+ appBar: AppBar(title: Text('Monero Light Wallet')),
+ body: SafeArea(
+ child: Center(
+ child: FilledButton.icon(
+ onPressed: _promptUnlock,
+ label: Text(i18n.unlockButton),
+ icon: Icon(Icons.lock_open),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/services/shared_preferences_service.dart b/lib/services/shared_preferences_service.dart
index 3ee0dd5..9553c18 100644
--- a/lib/services/shared_preferences_service.dart
+++ b/lib/services/shared_preferences_service.dart
@@ -5,6 +5,7 @@ class SharedPreferencesKeys {
static const String fiatCurrency = 'fiatCurrency';
static const String fiatRate = 'fiatRate';
static const String notificationsEnabled = 'notificationsEnabled';
+ static const String appLockEnabled = 'appLockEnabled';
static const String connectionAddress = 'connectionAddress';
static const String connectionProxyPort = 'connectionProxyPort';
static const String connectionUseTor = 'connectionUseTor';
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
index c2f0a8e..36ef1af 100644
--- a/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -7,6 +7,7 @@ import Foundation
import flutter_local_notifications
import flutter_secure_storage_macos
+import local_auth_darwin
import mobile_scanner
import path_provider_foundation
import screen_brightness_macos
@@ -16,6 +17,7 @@ import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
+ LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin"))
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
diff --git a/pubspec.lock b/pubspec.lock
index 0fa8f4b..11d4d87 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -171,6 +171,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ flutter_plugin_android_lifecycle:
+ dependency: transitive
+ description:
+ name: flutter_plugin_android_lifecycle
+ sha256: c2fe1001710127dfa7da89977a08d591398370d099aacdaa6d44da7eb14b8476
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.31"
flutter_riverpod:
dependency: "direct main"
description:
@@ -309,6 +317,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.1.1"
+ local_auth:
+ dependency: "direct main"
+ description:
+ name: local_auth
+ sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.0"
+ local_auth_android:
+ dependency: transitive
+ description:
+ name: local_auth_android
+ sha256: b2446c74fab1db37f828d4c54adaa3f003df80a29f5cbd710bbb8883d302e991
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.55"
+ local_auth_darwin:
+ dependency: transitive
+ description:
+ name: local_auth_darwin
+ sha256: "699873970067a40ef2f2c09b4c72eb1cfef64224ef041b3df9fdc5c4c1f91f49"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.6.1"
+ local_auth_platform_interface:
+ dependency: transitive
+ description:
+ name: local_auth_platform_interface
+ sha256: f98b8e388588583d3f781f6806e4f4c9f9e189d898d27f0c249b93a1973dd122
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ local_auth_windows:
+ dependency: transitive
+ description:
+ name: local_auth_windows
+ sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.11"
matcher:
dependency: transitive
description:
@@ -915,5 +963,5 @@ packages:
source: hosted
version: "6.5.0"
sdks:
- dart: ">=3.8.1 <4.0.0"
- flutter: ">=3.32.0"
+ dart: ">=3.9.0 <4.0.0"
+ flutter: ">=3.35.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 2de61fb..3ec2167 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -65,6 +65,7 @@ dependencies:
flutter_svg: ^2.2.1
skeletonizer: ^2.1.0+1
flutter_secure_storage: ^9.2.4
+ local_auth: ^2.3.0
dev_dependencies:
flutter_test:
diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc
index 89ba8c2..36fb6c3 100644
--- a/windows/flutter/generated_plugin_registrant.cc
+++ b/windows/flutter/generated_plugin_registrant.cc
@@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h"
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
+#include <local_auth_windows/local_auth_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <screen_brightness_windows/screen_brightness_windows_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h>
@@ -15,6 +16,8 @@
void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
+ LocalAuthPluginRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("LocalAuthPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
ScreenBrightnessWindowsPluginRegisterWithRegistrar(
diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake
index 9a83522..0029c44 100644
--- a/windows/flutter/generated_plugins.cmake
+++ b/windows/flutter/generated_plugins.cmake
@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_windows
+ local_auth_windows
permission_handler_windows
screen_brightness_windows
share_plus
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.