diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index a80ccbeeec..5c93323785 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -544,7 +544,8 @@ proc finishAppLoading*(self: AppController) = self.startupModule = nil if not self.onboardingModule.isNil: - self.onboardingModule.onAppLoaded() + let account = self.accountsService.getLoggedInAccount() + self.onboardingModule.onAppLoaded(account.keyUid) self.onboardingModule = nil self.mainModule.checkAndPerformProfileMigrationIfNeeded() diff --git a/src/app/modules/onboarding/io_interface.nim b/src/app/modules/onboarding/io_interface.nim index c97c1eb295..28e79040e7 100644 --- a/src/app/modules/onboarding/io_interface.nim +++ b/src/app/modules/onboarding/io_interface.nim @@ -10,7 +10,7 @@ import app/modules/onboarding/post_onboarding/task method delete*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") -method onAppLoaded*(self: AccessInterface) {.base.} = +method onAppLoaded*(self: AccessInterface, keyUid: string) {.base.} = raise newException(ValueError, "No implementation available") method onNodeLogin*(self: AccessInterface, error: string, account: AccountDto, settings: SettingsDto) {.base.} = diff --git a/src/app/modules/onboarding/module.nim b/src/app/modules/onboarding/module.nim index 91c60286bb..f3cd1f69ad 100644 --- a/src/app/modules/onboarding/module.nim +++ b/src/app/modules/onboarding/module.nim @@ -69,8 +69,8 @@ method delete*[T](self: Module[T]) = self.viewVariant.delete self.controller.delete -method onAppLoaded*[T](self: Module[T]) = - self.view.appLoaded() +method onAppLoaded*[T](self: Module[T], keyUid: string) = + self.view.appLoaded(keyUid) singletonInstance.engine.setRootContextProperty("onboardingModule", newQVariant()) self.view.delete self.view = nil diff --git a/src/app/modules/onboarding/view.nim b/src/app/modules/onboarding/view.nim index 1dd494b4a7..0c2ff26dd4 100644 --- a/src/app/modules/onboarding/view.nim +++ b/src/app/modules/onboarding/view.nim @@ -34,7 +34,7 @@ QtObject: ### QtSignals ### - proc appLoaded*(self: View) {.signal.} + proc appLoaded*(self: View, keyUid: string) {.signal.} proc accountLoginError*(self: View, error: string, wrongPassword: bool) {.signal.} ### QtProperties ### diff --git a/storybook/pages/OnboardingLayoutPage.qml b/storybook/pages/OnboardingLayoutPage.qml index ecff586f81..f75306522c 100644 --- a/storybook/pages/OnboardingLayoutPage.qml +++ b/storybook/pages/OnboardingLayoutPage.qml @@ -174,11 +174,9 @@ SplitView { } } - biometricsAvailable: ctrlBiometrics.checked - isBiometricsLogin: ctrlTouchIdUser.checked + keychain: keychain - onBiometricsRequested: (profileId) => biometricsPopup.open() - onDismissBiometricsRequested: biometricsPopup.close() + biometricsAvailable: ctrlBiometrics.checked onFinished: (flow, data) => { console.warn("!!! ONBOARDING FINISHED; flow:", flow, "; data:", JSON.stringify(data)) @@ -348,16 +346,22 @@ SplitView { } } - BiometricsPopup { - id: biometricsPopup + KeychainMock { + id: keychain - x: root.Window.width - width + parent: root - onObtainingPasswordSuccess: { + readonly property alias touchIdChecked: ctrlTouchIdUser.checked + onTouchIdCheckedChanged: onboarding.keychainChanged() + + function hasCredential(account) { const isKeycard = onboarding.currentPage instanceof LoginScreen && onboarding.currentPage.selectedProfileIsKeycard - onboarding.setBiometricResponse(isKeycard ? mockDriver.pin : mockDriver.password) + keychain.saveCredential(account, isKeycard ? mockDriver.pin : mockDriver.password) + + return touchIdChecked ? Keychain.StatusSuccess + : Keychain.StatusNotFound } } diff --git a/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml b/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml index 335d275de0..60c762da0f 100644 --- a/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml +++ b/ui/app/AppLayouts/Onboarding2/OnboardingFlow.qml @@ -31,7 +31,7 @@ OnboardingStackView { // functions required property var generateMnemonic - required property var isBiometricsLogin + required property var isBiometricsLogin // (string account) => bool required property var passwordStrengthScoreFunction required property var isSeedPhraseValid required property var isSeedPhraseDuplicate @@ -191,7 +191,8 @@ OnboardingStackView { keycardRemainingPukAttempts: root.remainingPukAttempts loginAccountsModel: root.loginAccountsModel - isBiometricsLogin: root.isBiometricsLogin(loginScreen.selectedProfileKeyId) + isBiometricsLogin: root.biometricsAvailable && + root.isBiometricsLogin(loginScreen.selectedProfileKeyId) onBiometricsRequested: (profileId) => root.biometricsRequested(profileId) onDismissBiometricsRequested: root.dismissBiometricsRequested() diff --git a/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml b/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml index 89f4a3d293..deb309c6ea 100644 --- a/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml +++ b/ui/app/AppLayouts/Onboarding2/OnboardingLayout.qml @@ -20,9 +20,9 @@ Page { required property OnboardingStore onboardingStore required property Keychain keychain - property bool biometricsAvailable: Qt.platform.os === Constants.mac - + property bool biometricsAvailable property bool networkChecksEnabled: true + property alias keycardPinInfoPageDelay: onboardingFlow.keycardPinInfoPageDelay readonly property alias stack: onboardingFlow // TODO remove external stack access diff --git a/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml b/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml index 4bd0a9db2b..42b076b8be 100644 --- a/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml +++ b/ui/app/AppLayouts/Onboarding2/stores/OnboardingStore.qml @@ -7,7 +7,7 @@ import AppLayouts.Onboarding.enums 1.0 QtObject { id: root - signal appLoaded + signal appLoaded(string keyUid) readonly property QtObject d: StatusQUtils.QObject { id: d diff --git a/ui/main.qml b/ui/main.qml index ab0dd1485e..52a3e88b48 100644 --- a/ui/main.qml +++ b/ui/main.qml @@ -425,7 +425,7 @@ StatusWindow { Keychain { service: "StatusDesktop" - id: keychain + id: appKeychain } Component { @@ -449,20 +449,41 @@ StatusWindow { anchors.fill: parent networkChecksEnabled: true + + // TODO: cover case when biometrics is globally disabled on mac biometricsAvailable: Qt.platform.os === Constants.mac - onboardingStore: onboardingStore - keychain: keychain + onboardingStore: OnboardingStore { + id: onboardingStore + + onAppLoaded: { + applicationWindow.appIsReady = true + applicationWindow.storeAppState() + moveToAppMain() + } + onAccountLoginError: function (error, wrongPassword) { + onboardingLayout.unwindToLoginScreen() // error handled internally + } + } + + keychain: appKeychain onFinished: (flow, data) => { const error = onboardingStore.finishOnboardingFlow(flow, data) - if (error != "") { + if (error !== "") { // We should never be here since everything should be validated already console.error("!!! ONBOARDING FINISHED WITH ERROR:", error) return } stack.push(splashScreenV2, { runningProgressAnimation: true }) + + if (!data.enableBiometrics) + return + + onboardingStore.appLoaded.connect((keyUid) => { + appKeychain.saveCredential(keyUid, data.password || data.pin) + }) } onLoginRequested: function (keyUid, method, data) { @@ -477,19 +498,6 @@ StatusWindow { } } onCurrentPageNameChanged: Global.addCentralizedMetricIfEnabled("navigation", {viewId: currentPageName}) - - OnboardingStore { - id: onboardingStore - - onAppLoaded: { - applicationWindow.appIsReady = true - applicationWindow.storeAppState() - moveToAppMain() - } - onAccountLoginError: function (error, wrongPassword) { - onboardingLayout.unwindToLoginScreen() // error handled internally - } - } } }