mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 11:59:30 +00:00
WIP: desktop password login and auth flow
This commit is contained in:
parent
f2bef21853
commit
a50520b37f
5 changed files with 148 additions and 126 deletions
|
@ -43,23 +43,23 @@ class DB {
|
|||
|
||||
static bool _initialized = false;
|
||||
|
||||
late final Box<dynamic> _boxAddressBook;
|
||||
late final Box<String> _boxDebugInfo;
|
||||
late final Box<NodeModel> _boxNodeModels;
|
||||
late final Box<NodeModel> _boxPrimaryNodes;
|
||||
late final Box<dynamic> _boxAllWalletsData;
|
||||
late final Box<NotificationModel> _boxNotifications;
|
||||
late final Box<NotificationModel> _boxWatchedTransactions;
|
||||
late final Box<NotificationModel> _boxWatchedTrades;
|
||||
late final Box<ExchangeTransaction> _boxTrades;
|
||||
late final Box<Trade> _boxTradesV2;
|
||||
late final Box<String> _boxTradeNotes;
|
||||
late final Box<String> _boxFavoriteWallets;
|
||||
late final Box<xmr.WalletInfo> _walletInfoSource;
|
||||
late final Box<dynamic> _boxPrefs;
|
||||
late final Box<TradeWalletLookup> _boxTradeLookup;
|
||||
late final Box<dynamic> _boxDBInfo;
|
||||
late final Box<String> _boxDesktopData;
|
||||
Box<dynamic>? _boxAddressBook;
|
||||
Box<String>? _boxDebugInfo;
|
||||
Box<NodeModel>? _boxNodeModels;
|
||||
Box<NodeModel>? _boxPrimaryNodes;
|
||||
Box<dynamic>? _boxAllWalletsData;
|
||||
Box<NotificationModel>? _boxNotifications;
|
||||
Box<NotificationModel>? _boxWatchedTransactions;
|
||||
Box<NotificationModel>? _boxWatchedTrades;
|
||||
Box<ExchangeTransaction>? _boxTrades;
|
||||
Box<Trade>? _boxTradesV2;
|
||||
Box<String>? _boxTradeNotes;
|
||||
Box<String>? _boxFavoriteWallets;
|
||||
Box<xmr.WalletInfo>? _walletInfoSource;
|
||||
Box<dynamic>? _boxPrefs;
|
||||
Box<TradeWalletLookup>? _boxTradeLookup;
|
||||
Box<dynamic>? _boxDBInfo;
|
||||
Box<String>? _boxDesktopData;
|
||||
|
||||
final Map<String, Box<dynamic>> _walletBoxes = {};
|
||||
|
||||
|
@ -68,7 +68,7 @@ class DB {
|
|||
final Map<Coin, Box<dynamic>> _usedSerialsCacheBoxes = {};
|
||||
|
||||
// exposed for monero
|
||||
Box<xmr.WalletInfo> get moneroWalletInfoBox => _walletInfoSource;
|
||||
Box<xmr.WalletInfo> get moneroWalletInfoBox => _walletInfoSource!;
|
||||
|
||||
// mutex for stack backup
|
||||
final mutex = Mutex();
|
||||
|
@ -124,6 +124,12 @@ class DB {
|
|||
_boxAllWalletsData = await Hive.openBox<dynamic>(boxNameAllWalletsData);
|
||||
}
|
||||
|
||||
if (Hive.isBoxOpen(boxNameDesktopData)) {
|
||||
_boxDesktopData = Hive.box<String>(boxNameDesktopData);
|
||||
} else {
|
||||
_boxDesktopData = await Hive.openBox<String>(boxNameDesktopData);
|
||||
}
|
||||
|
||||
_boxNotifications =
|
||||
await Hive.openBox<NotificationModel>(boxNameNotifications);
|
||||
_boxWatchedTransactions =
|
||||
|
@ -147,11 +153,11 @@ class DB {
|
|||
_initialized = true;
|
||||
|
||||
try {
|
||||
if (_boxPrefs.get("familiarity") == null) {
|
||||
await _boxPrefs.put("familiarity", 0);
|
||||
if (_boxPrefs!.get("familiarity") == null) {
|
||||
await _boxPrefs!.put("familiarity", 0);
|
||||
}
|
||||
int count = _boxPrefs.get("familiarity") as int;
|
||||
await _boxPrefs.put("familiarity", count + 1);
|
||||
int count = _boxPrefs!.get("familiarity") as int;
|
||||
await _boxPrefs!.put("familiarity", count + 1);
|
||||
Constants.exchangeForExperiencedUsers(count + 1);
|
||||
} catch (e, s) {
|
||||
print("$e $s");
|
||||
|
@ -160,7 +166,7 @@ class DB {
|
|||
}
|
||||
|
||||
Future<void> _loadWalletBoxes() async {
|
||||
final names = _boxAllWalletsData.get("names") as Map? ?? {};
|
||||
final names = _boxAllWalletsData!.get("names") as Map? ?? {};
|
||||
names.removeWhere((name, dyn) {
|
||||
final jsonObject = Map<String, dynamic>.from(dyn as Map);
|
||||
try {
|
||||
|
|
153
lib/main.dart
153
lib/main.dart
|
@ -48,19 +48,16 @@ import 'package:stackwallet/services/node_service.dart';
|
|||
import 'package:stackwallet/services/notifications_api.dart';
|
||||
import 'package:stackwallet/services/notifications_service.dart';
|
||||
import 'package:stackwallet/services/trade_service.dart';
|
||||
import 'package:stackwallet/services/wallets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/db_version_migration.dart';
|
||||
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
|
||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:stackwallet/utilities/theme/color_theme.dart';
|
||||
import 'package:stackwallet/utilities/theme/dark_colors.dart';
|
||||
import 'package:stackwallet/utilities/theme/light_colors.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:window_size/window_size.dart';
|
||||
|
||||
final openedFromSWBFileStringStateProvider =
|
||||
|
@ -221,8 +218,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
static const platform = MethodChannel("STACK_WALLET_RESTORE");
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
late final Wallets _wallets;
|
||||
late final Prefs _prefs;
|
||||
// late final Wallets _wallets;
|
||||
// late final Prefs _prefs;
|
||||
late final NotificationsService _notificationsService;
|
||||
late final NodeService _nodeService;
|
||||
late final TradesService _tradesService;
|
||||
|
@ -232,6 +229,16 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
bool didLoad = false;
|
||||
bool _desktopHasPassword = false;
|
||||
|
||||
Future<void> loadShared() async {
|
||||
await DB.instance.init();
|
||||
await ref.read(prefsChangeNotifierProvider).init();
|
||||
|
||||
if (Util.isDesktop) {
|
||||
_desktopHasPassword =
|
||||
await ref.read(storageCryptoHandlerProvider).hasPassword();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
try {
|
||||
if (didLoad) {
|
||||
|
@ -239,19 +246,15 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
}
|
||||
didLoad = true;
|
||||
|
||||
await DB.instance.init();
|
||||
await _prefs.init();
|
||||
|
||||
if (Util.isDesktop) {
|
||||
_desktopHasPassword =
|
||||
await ref.read(storageCryptoHandlerProvider).hasPassword();
|
||||
if (!Util.isDesktop) {
|
||||
await loadShared();
|
||||
}
|
||||
|
||||
_notificationsService = ref.read(notificationsProvider);
|
||||
_nodeService = ref.read(nodeServiceChangeNotifierProvider);
|
||||
_tradesService = ref.read(tradesServiceProvider);
|
||||
|
||||
NotificationApi.prefs = _prefs;
|
||||
NotificationApi.prefs = ref.read(prefsChangeNotifierProvider);
|
||||
NotificationApi.notificationsService = _notificationsService;
|
||||
|
||||
unawaited(ref.read(baseCurrenciesProvider).update());
|
||||
|
@ -260,23 +263,25 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
await _notificationsService.init(
|
||||
nodeService: _nodeService,
|
||||
tradesService: _tradesService,
|
||||
prefs: _prefs,
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
);
|
||||
ref.read(priceAnd24hChangeNotifierProvider).start(true);
|
||||
await _wallets.load(_prefs);
|
||||
await ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.load(ref.read(prefsChangeNotifierProvider));
|
||||
loadingCompleter.complete();
|
||||
// TODO: this should probably run unawaited. Keep commented out for now as proper community nodes ui hasn't been implemented yet
|
||||
// unawaited(_nodeService.updateCommunityNodes());
|
||||
|
||||
// run without awaiting
|
||||
if (Constants.enableExchange &&
|
||||
_prefs.externalCalls &&
|
||||
await _prefs.isExternalCallsSet()) {
|
||||
ref.read(prefsChangeNotifierProvider).externalCalls &&
|
||||
await ref.read(prefsChangeNotifierProvider).isExternalCallsSet()) {
|
||||
unawaited(ExchangeDataLoadingService().loadAll(ref));
|
||||
}
|
||||
|
||||
if (_prefs.isAutoBackupEnabled) {
|
||||
switch (_prefs.backupFrequencyType) {
|
||||
if (ref.read(prefsChangeNotifierProvider).isAutoBackupEnabled) {
|
||||
switch (ref.read(prefsChangeNotifierProvider).backupFrequencyType) {
|
||||
case BackupFrequencyType.everyTenMinutes:
|
||||
ref.read(autoSWBServiceProvider).startPeriodicBackupTimer(
|
||||
duration: const Duration(minutes: 10));
|
||||
|
@ -316,9 +321,6 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
.read(localeServiceChangeNotifierProvider.notifier)
|
||||
.loadLocale(notify: false);
|
||||
|
||||
_prefs = ref.read(prefsChangeNotifierProvider);
|
||||
_wallets = ref.read(walletsChangeNotifierProvider);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
ref.read(colorThemeProvider.state).state =
|
||||
StackColors.fromStackColorTheme(
|
||||
|
@ -423,7 +425,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
}
|
||||
|
||||
Future<void> goToRestoreSWB(String encrypted) async {
|
||||
if (!_prefs.hasPin) {
|
||||
if (!ref.read(prefsChangeNotifierProvider).hasPin) {
|
||||
await Navigator.of(navigatorKey.currentContext!)
|
||||
.pushNamed(CreatePinView.routeName, arguments: true)
|
||||
.then((value) {
|
||||
|
@ -569,57 +571,70 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
_buildOutlineInputBorder(colorScheme.textFieldDefaultBG),
|
||||
),
|
||||
),
|
||||
home: ConditionalParent(
|
||||
condition: Util.isDesktop,
|
||||
builder: (child) {
|
||||
return child;
|
||||
},
|
||||
child: FutureBuilder(
|
||||
future: load(),
|
||||
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
// FlutterNativeSplash.remove();
|
||||
if (Util.isDesktop &&
|
||||
(_wallets.hasWallets || _desktopHasPassword)) {
|
||||
String? startupWalletId;
|
||||
if (ref.read(prefsChangeNotifierProvider).gotoWalletOnStartup) {
|
||||
startupWalletId =
|
||||
ref.read(prefsChangeNotifierProvider).startupWalletId;
|
||||
home: Util.isDesktop
|
||||
? FutureBuilder(
|
||||
future: loadShared(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (_desktopHasPassword) {
|
||||
String? startupWalletId;
|
||||
if (ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.gotoWalletOnStartup) {
|
||||
startupWalletId =
|
||||
ref.read(prefsChangeNotifierProvider).startupWalletId;
|
||||
}
|
||||
|
||||
return DesktopLoginView(
|
||||
startupWalletId: startupWalletId,
|
||||
load: load,
|
||||
);
|
||||
} else {
|
||||
return const IntroView();
|
||||
}
|
||||
} else {
|
||||
return const LoadingView();
|
||||
}
|
||||
},
|
||||
)
|
||||
: FutureBuilder(
|
||||
future: load(),
|
||||
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
// FlutterNativeSplash.remove();
|
||||
if (ref.read(walletsChangeNotifierProvider).hasWallets ||
|
||||
ref.read(prefsChangeNotifierProvider).hasPin) {
|
||||
// return HomeView();
|
||||
|
||||
return DesktopLoginView(startupWalletId: startupWalletId);
|
||||
} else if (!Util.isDesktop &&
|
||||
(_wallets.hasWallets || _prefs.hasPin)) {
|
||||
// return HomeView();
|
||||
String? startupWalletId;
|
||||
if (ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.gotoWalletOnStartup) {
|
||||
startupWalletId =
|
||||
ref.read(prefsChangeNotifierProvider).startupWalletId;
|
||||
}
|
||||
|
||||
String? startupWalletId;
|
||||
if (ref.read(prefsChangeNotifierProvider).gotoWalletOnStartup) {
|
||||
startupWalletId =
|
||||
ref.read(prefsChangeNotifierProvider).startupWalletId;
|
||||
return LockscreenView(
|
||||
isInitialAppLogin: true,
|
||||
routeOnSuccess: HomeView.routeName,
|
||||
routeOnSuccessArguments: startupWalletId,
|
||||
biometricsAuthenticationTitle: "Unlock Stack",
|
||||
biometricsLocalizedReason:
|
||||
"Unlock your stack wallet using biometrics",
|
||||
biometricsCancelButtonString: "Cancel",
|
||||
);
|
||||
} else {
|
||||
return const IntroView();
|
||||
}
|
||||
} else {
|
||||
// CURRENTLY DISABLED as cannot be animated
|
||||
// technically not needed as FlutterNativeSplash will overlay
|
||||
// anything returned here until the future completes but
|
||||
// FutureBuilder requires you to return something
|
||||
return const LoadingView();
|
||||
}
|
||||
|
||||
return LockscreenView(
|
||||
isInitialAppLogin: true,
|
||||
routeOnSuccess: HomeView.routeName,
|
||||
routeOnSuccessArguments: startupWalletId,
|
||||
biometricsAuthenticationTitle: "Unlock Stack",
|
||||
biometricsLocalizedReason:
|
||||
"Unlock your stack wallet using biometrics",
|
||||
biometricsCancelButtonString: "Cancel",
|
||||
);
|
||||
} else {
|
||||
return const IntroView();
|
||||
}
|
||||
} else {
|
||||
// CURRENTLY DISABLED as cannot be animated
|
||||
// technically not needed as FlutterNativeSplash will overlay
|
||||
// anything returned here until the future completes but
|
||||
// FutureBuilder requires you to return something
|
||||
return const LoadingView();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,11 +21,13 @@ class DesktopLoginView extends ConsumerStatefulWidget {
|
|||
const DesktopLoginView({
|
||||
Key? key,
|
||||
this.startupWalletId,
|
||||
this.load,
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/desktopLogin";
|
||||
|
||||
final String? startupWalletId;
|
||||
final Future<void> Function()? load;
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopLoginView> createState() => _DesktopLoginViewState();
|
||||
|
@ -39,6 +41,32 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
|
|||
bool hidePassword = true;
|
||||
bool _continueEnabled = false;
|
||||
|
||||
Future<void> login() async {
|
||||
try {
|
||||
await ref
|
||||
.read(storageCryptoHandlerProvider)
|
||||
.initFromExisting(passwordController.text);
|
||||
|
||||
await widget.load?.call();
|
||||
|
||||
// if no errors passphrase is correct
|
||||
if (mounted) {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
DesktopHomeView.routeName,
|
||||
(route) => false,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
await showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: e.toString(),
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
passwordController = TextEditingController();
|
||||
|
@ -159,29 +187,7 @@ class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
|
|||
PrimaryButton(
|
||||
label: "Continue",
|
||||
enabled: _continueEnabled,
|
||||
onPressed: () async {
|
||||
try {
|
||||
await ref
|
||||
.read(storageCryptoHandlerProvider)
|
||||
.initFromExisting(passwordController.text);
|
||||
|
||||
// if no errors passphrase is correct
|
||||
if (mounted) {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
DesktopHomeView.routeName,
|
||||
(route) => false,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
await showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: e.toString(),
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
},
|
||||
onPressed: login,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 60,
|
||||
|
|
|
@ -5,13 +5,13 @@ import 'package:dropdown_button2/dropdown_button2.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
|
||||
|
@ -35,13 +35,8 @@ import 'package:zxcvbn/zxcvbn.dart';
|
|||
class CreateAutoBackup extends ConsumerStatefulWidget {
|
||||
const CreateAutoBackup({
|
||||
Key? key,
|
||||
this.secureStore = const SecureStorageWrapper(
|
||||
FlutterSecureStorage(),
|
||||
),
|
||||
}) : super(key: key);
|
||||
|
||||
final FlutterSecureStorageInterface secureStore;
|
||||
|
||||
@override
|
||||
ConsumerState<CreateAutoBackup> createState() => _CreateAutoBackup();
|
||||
}
|
||||
|
@ -51,7 +46,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
|||
late final TextEditingController passphraseController;
|
||||
late final TextEditingController passphraseRepeatController;
|
||||
|
||||
late final FlutterSecureStorageInterface secureStore;
|
||||
late final SecureStorageInterface secureStore;
|
||||
|
||||
late final StackFileSystem stackFileSystem;
|
||||
late final FocusNode passphraseFocusNode;
|
||||
|
@ -85,7 +80,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
secureStore = widget.secureStore;
|
||||
secureStore = ref.read(secureStoreProvider);
|
||||
stackFileSystem = StackFileSystem();
|
||||
|
||||
fileLocationController = TextEditingController();
|
||||
|
@ -686,7 +681,9 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
|||
final String fileToSave =
|
||||
createAutoBackupFilename(pathToSave, now);
|
||||
|
||||
final backup = await SWB.createStackWalletJSON();
|
||||
final backup = await SWB.createStackWalletJSON(
|
||||
secureStorage: secureStore,
|
||||
);
|
||||
|
||||
bool result = await SWB.encryptStackWalletWithADK(
|
||||
fileToSave,
|
||||
|
|
|
@ -89,12 +89,10 @@ class DPS {
|
|||
}
|
||||
|
||||
Future<bool> hasPassword() async {
|
||||
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
|
||||
final keyBlob = DB.instance.get<String>(
|
||||
boxName: DB.boxNameDesktopData,
|
||||
key: _kKeyBlobKey,
|
||||
);
|
||||
await box.close();
|
||||
return keyBlob != null;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue