diff --git a/cw_core/lib/set_app_secure_native.dart b/cw_core/lib/set_app_secure_native.dart index 2f2e9a9c9..09e01556c 100644 --- a/cw_core/lib/set_app_secure_native.dart +++ b/cw_core/lib/set_app_secure_native.dart @@ -2,5 +2,6 @@ import 'package:flutter/services.dart'; const utils = const MethodChannel('com.cake_wallet/native_utils'); -void setIsAppSecureNative(bool isAppSecure) => - utils.invokeMethod('setIsAppSecure', {'isAppSecure': isAppSecure}); +void setIsAppSecureNative(bool isAppSecure) { + utils.invokeMethod('setIsAppSecure', {'isAppSecure': isAppSecure}); +} \ No newline at end of file diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index ec95060d1..401509606 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -15,6 +15,8 @@ import UnstoppableDomainsResolution if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate } + + makeSecure() let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let legacyMigrationChannel = FlutterMethodChannel( @@ -96,22 +98,39 @@ import UnstoppableDomainsResolution result(address) } - + case "setIsAppSecure": + guard let args = call.arguments as? Dictionary, + let isAppSecure = args["isAppSecure"] else { + result(nil) + return + } + + if isAppSecure { + self?.textField.isSecureTextEntry = true + } else { + self?.textField.isSecureTextEntry = false + } + + result(nil) default: result(FlutterMethodNotImplemented) } }) - + GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + private var textField = UITextField() + + private func makeSecure() { + if (!self.window.subviews.contains(textField)) { + self.window.addSubview(textField) + textField.centerYAnchor.constraint(equalTo: self.window.centerYAnchor).isActive = true + textField.centerXAnchor.constraint(equalTo: self.window.centerXAnchor).isActive = true + self.window.layer.superlayer?.addSublayer(textField.layer) + textField.layer.sublayers?.first?.addSublayer(self.window.layer) + } + } - override func applicationWillResignActive(_: UIApplication ) { - self.window?.rootViewController?.view.endEditing(true) - self.window?.isHidden = true; - } - - override func applicationDidBecomeActive(_: UIApplication) { - self.window?.isHidden = false; - } } diff --git a/lib/core/auth_service.dart b/lib/core/auth_service.dart index d26fd17a3..8091740e6 100644 --- a/lib/core/auth_service.dart +++ b/lib/core/auth_service.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/core/totp_request_details.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:flutter/material.dart'; @@ -9,6 +10,8 @@ import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/entities/encrypt.dart'; import 'package:cake_wallet/store/settings_store.dart'; +import '../src/screens/setup_2fa/setup_2fa_enter_code_page.dart'; + class AuthService with Store { AuthService({ required this.secureStorage, @@ -20,6 +23,8 @@ class AuthService with Store { Routes.showKeys, Routes.backup, Routes.setupPin, + Routes.setup_2faPage, + Routes.modify2FAPage, ]; final FlutterSecureStorage secureStorage; @@ -79,6 +84,7 @@ class AuthService with Store { {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); @@ -90,17 +96,43 @@ class AuthService with Store { } 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); + if (settingsStore.useTOTP2FA) { + auth.close( + route: Routes.totpAuthCodePage, + arguments: TotpAuthArgumentsModel( + isForSetup: !settingsStore.useTOTP2FA, + onTotpAuthenticationFinished: + (bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuth) async { + if (!isAuthenticatedSuccessfully) { + onAuthSuccess?.call(false); + return; + } + if (onAuthSuccess != null) { + totpAuth.close().then((value) => onAuthSuccess.call(true)); + } else { + totpAuth.close(route: route, arguments: arguments); + } + }, + ), + ); + } else { + if (onAuthSuccess != null) { + auth.close().then((value) => onAuthSuccess.call(true)); + } else { + auth.close(route: route, arguments: arguments); + } + } } - }); + + }); + } } diff --git a/lib/core/totp_request_details.dart b/lib/core/totp_request_details.dart new file mode 100644 index 000000000..63da06455 --- /dev/null +++ b/lib/core/totp_request_details.dart @@ -0,0 +1,13 @@ +import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart'; + +class TotpAuthArgumentsModel { + final bool? isForSetup; + final bool? isClosable; + final OnTotpAuthenticationFinished? onTotpAuthenticationFinished; + + TotpAuthArgumentsModel({ + this.isForSetup, + this.isClosable, + this.onTotpAuthenticationFinished, + }); +} diff --git a/lib/di.dart b/lib/di.dart index 1667fd1d2..0f59c702f 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -10,6 +10,7 @@ import 'package:cake_wallet/entities/receive_page_option.dart'; import 'package:cake_wallet/ionia/ionia_anypay.dart'; import 'package:cake_wallet/ionia/ionia_gift_card.dart'; import 'package:cake_wallet/ionia/ionia_tip.dart'; +import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart'; import 'package:cake_wallet/src/screens/buy/onramper_page.dart'; import 'package:cake_wallet/src/screens/buy/payfura_page.dart'; @@ -27,6 +28,10 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dar import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart'; import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart'; +import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart'; +import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart'; +import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart'; +import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart'; import 'package:cake_wallet/themes/theme_list.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; @@ -53,8 +58,8 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart'; import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart'; +import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart'; import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart'; -import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; @@ -187,6 +192,8 @@ import 'package:cake_wallet/core/wallet_loading_service.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/entities/qr_view_data.dart'; +import 'core/totp_request_details.dart'; + final getIt = GetIt.instance; var _isSetupFinished = false; @@ -201,18 +208,18 @@ late Box _ordersSource; late Box? _unspentCoinsInfoSource; late Box _anonpayInvoiceInfoSource; -Future setup( - {required Box walletInfoSource, - required Box nodeSource, - required Box contactSource, - required Box tradesSource, - required Box