mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-11-17 01:37:40 +00:00
CW-278 Enhance PIN timeout feature code (#886)
* CW-278 enhance pin timeout feature * CW-278 enhance pin timeout feature * Update flow to remove extension * Replace pin request on other instances
This commit is contained in:
parent
27961f2f25
commit
efef30f8eb
5 changed files with 102 additions and 111 deletions
|
@ -1,10 +1,12 @@
|
|||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/secret_store_key.dart';
|
||||
import 'package:cake_wallet/entities/encrypt.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
|
||||
class AuthService with Store {
|
||||
|
@ -14,6 +16,12 @@ class AuthService with Store {
|
|||
required this.settingsStore,
|
||||
});
|
||||
|
||||
static const List<String> _alwaysAuthenticateRoutes = [
|
||||
Routes.showKeys,
|
||||
Routes.backup,
|
||||
Routes.setupPin,
|
||||
];
|
||||
|
||||
final FlutterSecureStorage secureStorage;
|
||||
final SharedPreferences sharedPreferences;
|
||||
final SettingsStore settingsStore;
|
||||
|
@ -66,4 +74,33 @@ class AuthService with Store {
|
|||
|
||||
return timeDifference.inMinutes;
|
||||
}
|
||||
|
||||
Future<void> authenticateAction(BuildContext context,
|
||||
{Function(bool)? onAuthSuccess, String? route, Object? arguments}) async {
|
||||
assert(route != null || onAuthSuccess != null,
|
||||
'Either route or onAuthSuccess param must be passed.');
|
||||
if (!requireAuth() && !_alwaysAuthenticateRoutes.contains(route)) {
|
||||
if (onAuthSuccess != null) {
|
||||
onAuthSuccess(true);
|
||||
} else {
|
||||
Navigator.of(context).pushNamed(
|
||||
route ?? '',
|
||||
arguments: arguments,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
onAuthSuccess?.call(false);
|
||||
return;
|
||||
}
|
||||
if (onAuthSuccess != null) {
|
||||
auth.close().then((value) => onAuthSuccess.call(true));
|
||||
} else {
|
||||
auth.close(route: route, arguments: arguments);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -499,7 +499,7 @@ Future setup(
|
|||
}
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
WalletListPage(walletListViewModel: getIt.get<WalletListViewModel>()));
|
||||
WalletListPage(walletListViewModel: getIt.get<WalletListViewModel>(), authService: getIt.get<AuthService>(),));
|
||||
|
||||
getIt.registerFactory(() {
|
||||
final wallet = getIt.get<AppStore>().wallet!;
|
||||
|
@ -593,7 +593,7 @@ Future setup(
|
|||
|
||||
getIt.registerFactory(() => ConnectionSyncPage(getIt.get<NodeListViewModel>(), getIt.get<DashboardViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => SecurityBackupPage(getIt.get<SecuritySettingsViewModel>()));
|
||||
getIt.registerFactory(() => SecurityBackupPage(getIt.get<SecuritySettingsViewModel>(), getIt.get<AuthService>()));
|
||||
|
||||
getIt.registerFactory(() => PrivacyPage(getIt.get<PrivacySettingsViewModel>()));
|
||||
|
||||
|
@ -926,7 +926,7 @@ Future setup(
|
|||
wallet: getIt.get<AppStore>().wallet!)
|
||||
);
|
||||
|
||||
getIt.registerFactory(() => DesktopWalletSelectionDropDown(getIt.get<WalletListViewModel>()));
|
||||
getIt.registerFactory(() => DesktopWalletSelectionDropDown(getIt.get<WalletListViewModel>(), getIt.get<AuthService>()));
|
||||
|
||||
getIt.registerFactory(() => DesktopSidebarViewModel());
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import 'package:another_flushbar/flushbar.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/desktop_dropdown_item.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
|
@ -16,8 +17,10 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
|||
|
||||
class DesktopWalletSelectionDropDown extends StatefulWidget {
|
||||
final WalletListViewModel walletListViewModel;
|
||||
final AuthService _authService;
|
||||
|
||||
DesktopWalletSelectionDropDown(this.walletListViewModel, {Key? key}) : super(key: key);
|
||||
DesktopWalletSelectionDropDown(this.walletListViewModel, this._authService, {Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<DesktopWalletSelectionDropDown> createState() => _DesktopWalletSelectionDropDownState();
|
||||
|
@ -140,25 +143,12 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
|||
}
|
||||
|
||||
Future<void> _loadWallet(WalletListItem wallet) async {
|
||||
if (await widget.walletListViewModel.checkIfAuthRequired()) {
|
||||
await Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
widget._authService.authenticateAction(context,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auth.changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
|
||||
await widget.walletListViewModel.loadWallet(wallet);
|
||||
auth.hideProgressText();
|
||||
auth.close();
|
||||
setState(() {});
|
||||
} catch (e) {
|
||||
auth.changeProcessText(
|
||||
S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
|
||||
await widget.walletListViewModel.loadWallet(wallet);
|
||||
|
@ -167,7 +157,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
|||
} catch (e) {
|
||||
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _navigateToCreateWallet() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/entities/pin_code_required_duration.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
||||
|
@ -13,7 +13,9 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class SecurityBackupPage extends BasePage {
|
||||
SecurityBackupPage(this._securitySettingsViewModel);
|
||||
SecurityBackupPage(this._securitySettingsViewModel, this._authService);
|
||||
|
||||
final AuthService _authService;
|
||||
|
||||
@override
|
||||
String get title => S.current.security_and_backup;
|
||||
|
@ -27,35 +29,24 @@ class SecurityBackupPage extends BasePage {
|
|||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.show_keys,
|
||||
handler: (_) => Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
auth.close(route: Routes.showKeys);
|
||||
}
|
||||
}),
|
||||
handler: (_) => _authService.authenticateAction(context, route: Routes.showKeys),
|
||||
),
|
||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.create_backup,
|
||||
handler: (_) => Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
auth.close(route: Routes.backup);
|
||||
}
|
||||
}),
|
||||
handler: (_) => _authService.authenticateAction(context, route: Routes.backup),
|
||||
),
|
||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.settings_change_pin,
|
||||
handler: (_) => Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
|
||||
auth.close(
|
||||
route: isAuthenticatedSuccessfully ? Routes.setupPin : null,
|
||||
arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) {
|
||||
setupPinContext.close();
|
||||
},
|
||||
);
|
||||
})),
|
||||
title: S.current.settings_change_pin,
|
||||
handler: (_) => _authService.authenticateAction(
|
||||
context,
|
||||
route: Routes.setupPin,
|
||||
arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) {
|
||||
setupPinContext.close();
|
||||
},
|
||||
),
|
||||
),
|
||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
Observer(builder: (_) {
|
||||
return SettingsSwitcherCell(
|
||||
|
@ -63,8 +54,8 @@ class SecurityBackupPage extends BasePage {
|
|||
value: _securitySettingsViewModel.allowBiometricalAuthentication,
|
||||
onValueChange: (BuildContext context, bool value) {
|
||||
if (value) {
|
||||
Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
_authService.authenticateAction(context,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
if (await _securitySettingsViewModel.biometricAuthenticated()) {
|
||||
_securitySettingsViewModel
|
||||
|
@ -74,8 +65,6 @@ class SecurityBackupPage extends BasePage {
|
|||
_securitySettingsViewModel
|
||||
.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
|
||||
}
|
||||
|
||||
auth.close();
|
||||
});
|
||||
} else {
|
||||
_securitySettingsViewModel.setAllowBiometricalAuthentication(value);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
|
@ -19,18 +19,21 @@ import 'package:flutter_slidable/flutter_slidable.dart';
|
|||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
|
||||
class WalletListPage extends BasePage {
|
||||
WalletListPage({required this.walletListViewModel});
|
||||
WalletListPage({required this.walletListViewModel, required this.authService});
|
||||
|
||||
final WalletListViewModel walletListViewModel;
|
||||
final AuthService authService;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletListBody(walletListViewModel: walletListViewModel);
|
||||
Widget body(BuildContext context) =>
|
||||
WalletListBody(walletListViewModel: walletListViewModel, authService: authService);
|
||||
}
|
||||
|
||||
class WalletListBody extends StatefulWidget {
|
||||
WalletListBody({required this.walletListViewModel});
|
||||
WalletListBody({required this.walletListViewModel, required this.authService});
|
||||
|
||||
final WalletListViewModel walletListViewModel;
|
||||
final AuthService authService;
|
||||
|
||||
@override
|
||||
WalletListBodyState createState() => WalletListBodyState();
|
||||
|
@ -129,7 +132,8 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
fontSize: 22,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme.headline6!
|
||||
.primaryTextTheme
|
||||
.headline6!
|
||||
.color!),
|
||||
)
|
||||
],
|
||||
|
@ -201,61 +205,40 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
}
|
||||
|
||||
Future<void> _loadWallet(WalletListItem wallet) async {
|
||||
if (await widget.walletListViewModel.checkIfAuthRequired()) {
|
||||
await Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
await widget.authService.authenticateAction(context,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auth.changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
|
||||
await widget.walletListViewModel.loadWallet(wallet);
|
||||
auth.hideProgressText();
|
||||
auth.close();
|
||||
// only pop the wallets route in mobile as it will go back to dashboard page
|
||||
// in desktop platforms the navigation tree is different
|
||||
if (DeviceInfo.instance.isMobile) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
auth.changeProcessText(
|
||||
S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
|
||||
await widget.walletListViewModel.loadWallet(wallet);
|
||||
hideProgressText();
|
||||
await hideProgressText();
|
||||
// only pop the wallets route in mobile as it will go back to dashboard page
|
||||
// in desktop platforms the navigation tree is different
|
||||
if (DeviceInfo.instance.isMobile) {
|
||||
Navigator.of(context).pop();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _removeWallet(WalletListItem wallet) async {
|
||||
if (widget.walletListViewModel.checkIfAuthRequired()) {
|
||||
await Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
_onSuccessfulAuth(wallet, auth);
|
||||
});
|
||||
} else {
|
||||
_onSuccessfulAuth(wallet, null);
|
||||
}
|
||||
widget.authService.authenticateAction(context,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
_onSuccessfulAuth(wallet);
|
||||
});
|
||||
}
|
||||
|
||||
void _onSuccessfulAuth(WalletListItem wallet, AuthPageState? auth) async {
|
||||
void _onSuccessfulAuth(WalletListItem wallet) async {
|
||||
bool confirmed = false;
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
|
@ -275,31 +258,23 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
|
||||
if (confirmed) {
|
||||
try {
|
||||
auth != null
|
||||
? auth.changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name))
|
||||
: changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name));
|
||||
changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name));
|
||||
await widget.walletListViewModel.remove(wallet);
|
||||
hideProgressText();
|
||||
} catch (e) {
|
||||
auth != null
|
||||
? auth.changeProcessText(
|
||||
S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()),
|
||||
)
|
||||
: changeProcessText(
|
||||
S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()),
|
||||
);
|
||||
changeProcessText(
|
||||
S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
auth?.close();
|
||||
}
|
||||
|
||||
void changeProcessText(String text) {
|
||||
_progressBar = createBar<void>(text, duration: null)..show(context);
|
||||
}
|
||||
|
||||
void hideProgressText() {
|
||||
Future.delayed(Duration(milliseconds: 50), () {
|
||||
Future<void> hideProgressText() async {
|
||||
await Future.delayed(Duration(milliseconds: 50), () {
|
||||
_progressBar?.dismiss();
|
||||
_progressBar = null;
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue