Onboarding: saving credentials to Keychain after account creation

Closes: #17085
This commit is contained in:
Michał Cieślak 2025-02-18 18:48:10 +01:00
parent c9ba6a25d6
commit cf2c221c02
9 changed files with 51 additions and 37 deletions

View File

@ -543,7 +543,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()

View File

@ -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.} =

View File

@ -65,8 +65,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

View File

@ -33,7 +33,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 ###

View File

@ -163,11 +163,9 @@ SplitView {
signal accountLoginError(string error, bool wrongPassword)
}
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))
@ -337,16 +335,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
}
}

View File

@ -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 validateConnectionString
@ -190,7 +190,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()

View File

@ -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

View File

@ -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

View File

@ -420,7 +420,7 @@ StatusWindow {
Keychain {
service: "StatusDesktop"
id: keychain
id: appKeychain
}
Component {
@ -444,21 +444,42 @@ 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.stack.pop()
}
}
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.clear()
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) {
@ -473,19 +494,6 @@ StatusWindow {
}
}
onCurrentPageNameChanged: Global.addCentralizedMetricIfEnabled("navigation", {viewId: currentPageName})
OnboardingStore {
id: onboardingStore
onAppLoaded: {
applicationWindow.appIsReady = true
applicationWindow.storeAppState()
moveToAppMain()
}
onAccountLoginError: function (error, wrongPassword) {
onboardingLayout.stack.pop()
}
}
}
}