From 43e062d1ac319ed127a0f656b68958de9e453107 Mon Sep 17 00:00:00 2001 From: Adegoke David <64401859+Blazebrain@users.noreply.github.com> Date: Wed, 17 May 2023 15:43:23 +0100 Subject: [PATCH] Cw-263-TOTP-2FA-In-Security-Settings (#892) * CW-263-TOTP-2FA-in-security-settings WIP * Implement TOTP 2FA WIP * Implement TOTP 2FA Authentication * chore: Remove unneeded formatting * revert formatting * fixes * CW-263-TOTP-2FA-in-security-settings WIP * Setup TOTP Complete, left with Modify TOTF * CW-263-TOTP-2FA-in-security-settings * CW-263-TOTP-2FA-in-security-settings * CW-263-TOTP-2FA-in-security-settings * fix: Add copy-to-clipboard for qr secret key * fix: Translation * chore: Move strings into translation files * feat: End to end flow for TOTP * hotfix: Switch totp to use sha512 * Update strings; 8 digits and error explanation * fix: Totp 2fa implementation feedback * hotfix: same action for button and alert close * feat: App should show both normal and totp auths when totp is enabled * hotfix: prevent barrier from dismissing * fix: Changes requested during PR review * - Minor Enhancements - Minor UI fixes --------- Co-authored-by: Justin Ehrenhofer Co-authored-by: OmarHatem --- lib/core/auth_service.dart | 42 +- lib/core/totp_request_details.dart | 13 + lib/di.dart | 613 +++++++++--------- lib/entities/preferences_key.dart | 3 + lib/router.dart | 48 +- lib/routes.dart | 4 + lib/src/screens/root/root.dart | 55 +- .../settings/security_backup_page.dart | 121 ++-- .../screens/setup_2fa/modify_2fa_page.dart | 55 ++ lib/src/screens/setup_2fa/setup_2fa.dart | 62 ++ .../setup_2fa/setup_2fa_enter_code_page.dart | 221 +++++++ .../screens/setup_2fa/setup_2fa_qr_page.dart | 145 +++++ .../widgets/popup_cancellable_alert.dart | 92 +++ lib/src/widgets/alert_close_button.dart | 5 +- lib/store/settings_store.dart | 297 +++++---- lib/utils/totp_utils.dart | 83 +++ lib/view_model/set_up_2fa_viewmodel.dart | 159 +++++ .../security_settings_view_model.dart | 3 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 +- pubspec_base.yaml | 1 + res/values/strings_ar.arb | 17 + res/values/strings_bg.arb | 17 + res/values/strings_cs.arb | 17 + res/values/strings_de.arb | 17 + res/values/strings_en.arb | 17 + res/values/strings_es.arb | 17 + res/values/strings_fr.arb | 17 + res/values/strings_hi.arb | 17 + res/values/strings_hr.arb | 22 + res/values/strings_id.arb | 28 + res/values/strings_it.arb | 17 + res/values/strings_ja.arb | 19 +- res/values/strings_ko.arb | 17 + res/values/strings_my.arb | 29 + res/values/strings_nl.arb | 17 + res/values/strings_pl.arb | 17 + res/values/strings_pt.arb | 17 + res/values/strings_ru.arb | 17 + res/values/strings_th.arb | 17 + res/values/strings_tr.arb | 17 + res/values/strings_uk.arb | 17 + res/values/strings_ur.arb | 17 + res/values/strings_zh.arb | 17 + 43 files changed, 1951 insertions(+), 494 deletions(-) create mode 100644 lib/core/totp_request_details.dart create mode 100644 lib/src/screens/setup_2fa/modify_2fa_page.dart create mode 100644 lib/src/screens/setup_2fa/setup_2fa.dart create mode 100644 lib/src/screens/setup_2fa/setup_2fa_enter_code_page.dart create mode 100644 lib/src/screens/setup_2fa/setup_2fa_qr_page.dart create mode 100644 lib/src/screens/setup_2fa/widgets/popup_cancellable_alert.dart create mode 100644 lib/utils/totp_utils.dart create mode 100644 lib/view_model/set_up_2fa_viewmodel.dart 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