Skip to content

Commit

Permalink
feat(@desktop/onboarding): Lost Keycard - start using account witho…
Browse files Browse the repository at this point in the history
…ut keycard

This commit introduces:
- `Start using account without keycard` flow

Closes: #7642
  • Loading branch information
saledjenic authored and alaibe committed Jan 30, 2023
1 parent b00f0a8 commit 841a37e
Show file tree
Hide file tree
Showing 29 changed files with 333 additions and 85 deletions.
3 changes: 1 addition & 2 deletions src/app/modules/shared_modules/keycard_popup/controller.nim
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,7 @@ proc convertSelectedKeyPairToKeycardAccount*(self: Controller, password: string)
return
let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true)
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW)
self.accountsService.convertToKeycardAccount(self.tmpSelectedKeyPairDto.keyUid,
currentPassword = password,
self.accountsService.convertToKeycardAccount(currentPassword = password,
newPassword = acc.derivedAccounts.encryption.publicKey)

proc getConvertingProfileSuccess*(self: Controller): bool =
Expand Down
34 changes: 29 additions & 5 deletions src/app/modules/startup/controller.nim
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ proc importMnemonic*(self: Controller): bool =
self.delegate.importAccountSuccess()
return true
else:
self.delegate.importAccountError(error)
self.delegate.emitStartupError(error, StartupErrorType.ImportAccError)
return false

proc setupKeychain(self: Controller, store: bool) =
Expand All @@ -352,7 +352,7 @@ proc setupAccount(self: Controller, accountId: string, storeToKeychain: bool) =
self.delegate.moveToLoadingAppState()
let error = self.accountsService.setupAccount(accountId, self.tmpPassword, self.tmpDisplayName)
if error != "":
self.delegate.setupAccountError(error)
self.delegate.emitStartupError(error, StartupErrorType.SetupAccError)
else:
self.setupKeychain(storeToKeychain)

Expand Down Expand Up @@ -388,7 +388,7 @@ proc setupKeycardAccount*(self: Controller, storeToKeychain: bool, newKeycard: b
else:
if self.tmpKeycardEvent.keyUid.len == 0 or
self.accountsService.openedAccountsContainsKeyUid(self.tmpKeycardEvent.keyUid):
self.delegate.importAccountError(ACCOUNT_ALREADY_EXISTS_ERROR)
self.delegate.emitStartupError(ACCOUNT_ALREADY_EXISTS_ERROR, StartupErrorType.ImportAccError)
return
self.delegate.moveToLoadingAppState()
if newKeycard:
Expand Down Expand Up @@ -441,19 +441,43 @@ proc login*(self: Controller) =
if(error.len > 0):
self.delegate.emitAccountLoginError(error)

proc loginAccountKeycard*(self: Controller, storeToKeychain = false, syncWalletAfterLogin = false) =
proc loginAccountKeycard*(self: Controller, storeToKeychainValue: string, syncWalletAfterLogin = false) =
if syncWalletAfterLogin:
self.syncKeycardBasedOnAppWalletStateAfterLogin()
singletonInstance.localAccountSettings.setStoreToKeychainValue(storeToKeychainValue)
self.delegate.moveToLoadingAppState()
let selAcc = self.getSelectedLoginAccount()
let error = self.accountsService.loginAccountKeycard(selAcc, self.tmpKeycardEvent)
if(error.len > 0):
self.delegate.emitAccountLoginError(error)

proc loginAccountKeycardUsingSeedPhrase*(self: Controller, storeToKeychain: bool) =
let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true, includeWhisper = true)
let selAcc = self.getSelectedLoginAccount()

var kcData = KeycardEvent(
keyUid: acc.keyUid,
masterKey: KeyDetails(address: acc.address),
whisperKey: KeyDetails(privateKey: acc.derivedAccounts.whisper.privateKey),
encryptionKey: KeyDetails(publicKey: acc.derivedAccounts.encryption.publicKey)
)
if acc.derivedAccounts.whisper.privateKey.startsWith("0x"):
kcData.whisperKey.privateKey = acc.derivedAccounts.whisper.privateKey[2..^1]

if storeToKeychain:
## storing not now, user will be asked to store the pin once he is logged in
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW)
else:
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NEVER)

self.delegate.moveToLoadingAppState()
let error = self.accountsService.loginAccountKeycard(self.tmpKeycardEvent)
let error = self.accountsService.loginAccountKeycard(selAcc, kcData)
if(error.len > 0):
self.delegate.emitAccountLoginError(error)

proc convertToRegularAccount*(self: Controller): string =
let acc = self.accountsService.createAccountFromMnemonic(self.getSeedPhrase(), includeEncryption = true)
return self.accountsService.convertToRegularAccount(self.getSeedPhrase(), acc.derivedAccounts.encryption.publicKey, self.getPassword())
proc getKeyUidForSeedPhrase*(self: Controller, seedPhrase: string): string =
let acc = self.accountsService.createAccountFromMnemonic(seedPhrase)
return acc.keyUid
Expand Down
9 changes: 8 additions & 1 deletion src/app/modules/startup/internal/biometrics_state.nim
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ method executePrimaryCommand*(self: BiometricsState, controller: Controller) =
elif self.flowType == FlowType.LostKeycardReplacement:
self.storeToKeychain = storeToKeychain
controller.startLoginFlowAutomatically(controller.getPin())
elif self.flowType == FlowType.LostKeycardConvertToRegularAccount:
controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain)

method executeSecondaryCommand*(self: BiometricsState, controller: Controller) =
let storeToKeychain = false # false, cause we don't have keychain support for other than mac os
Expand All @@ -49,11 +51,16 @@ method executeSecondaryCommand*(self: BiometricsState, controller: Controller) =
elif self.flowType == FlowType.LostKeycardReplacement:
self.storeToKeychain = storeToKeychain
controller.startLoginFlowAutomatically(controller.getPin())
elif self.flowType == FlowType.LostKeycardConvertToRegularAccount:
controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain)

method resolveKeycardNextState*(self: BiometricsState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
if self.flowType == FlowType.LostKeycardReplacement:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
controller.setKeycardEvent(keycardEvent)
controller.loginAccountKeycard(self.storeToKeychain, syncWalletAfterLogin = true)
var storeToKeychainValue = LS_VALUE_NEVER
if self.storeToKeychain:
storeToKeychainValue = LS_VALUE_NOT_NOW
controller.loginAccountKeycard(storeToKeychainValue, syncWalletAfterLogin = true)
3 changes: 2 additions & 1 deletion src/app/modules/startup/internal/keycard_enter_puk_state.nim
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,6 @@ method resolveKeycardNextState*(self: KeycardEnterPukState, keycardFlowType: str
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setKeycardEvent(keycardEvent)
controller.setPukValid(true)
controller.loginAccountKeycard()
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)
return nil
7 changes: 4 additions & 3 deletions src/app/modules/startup/internal/keycard_pin_set_state.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ method executePrimaryCommand*(self: KeycardPinSetState, controller: Controller)
controller.startLoginFlowAutomatically(controller.getPin())
return
if controller.getValidPuk():
controller.loginAccountKeycard()
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)
if self.flowType == FlowType.LostKeycardReplacement:
if main_constants.IS_MACOS:
return
Expand All @@ -56,10 +57,10 @@ method resolveKeycardNextState*(self: KeycardPinSetState, keycardFlowType: strin
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
controller.setKeycardEvent(keycardEvent)
controller.loginAccountKeycard(storeToKeychain = true, syncWalletAfterLogin = true)
controller.loginAccountKeycard(storeToKeychainValue = LS_VALUE_NOT_NOW, syncWalletAfterLogin = true)
if self.flowType == FlowType.AppLogin:
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
keycardEvent.error.len == 0:
# we are here in case of recover account from the login flow using seed phrase
controller.setKeycardEvent(keycardEvent)
controller.loginAccountKeycard(storeToKeychain = false, syncWalletAfterLogin = false)
controller.loginAccountKeycard(storeToKeychainValue = LS_VALUE_NEVER, syncWalletAfterLogin = false)
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,5 @@ proc delete*(self: KeycardWrongKeycardState) =

method executeBackCommand*(self: KeycardWrongKeycardState, controller: Controller) =
if self.flowType == FlowType.FirstRunOldUserKeycardImport or
self.flowType == FlowType.AppLogin or
self.flowType == FlowType.LostKeycardReplacement:
self.flowType == FlowType.AppLogin:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))
3 changes: 2 additions & 1 deletion src/app/modules/startup/internal/keycard_wrong_puk_state.nim
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,6 @@ method resolveKeycardNextState*(self: KeycardWrongPukState, keycardFlowType: str
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
controller.setKeycardEvent(keycardEvent)
controller.setPukValid(true)
controller.loginAccountKeycard()
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)
return nil
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
type
LoginKeycardConvertedToRegularAccountState* = ref object of State

proc newLoginKeycardConvertedToRegularAccountState*(flowType: FlowType, backState: State): LoginKeycardConvertedToRegularAccountState =
result = LoginKeycardConvertedToRegularAccountState()
result.setup(flowType, StateType.LoginKeycardConvertedToRegularAccount, backState)

proc delete*(self: LoginKeycardConvertedToRegularAccountState) =
self.State.delete

method executePrimaryCommand*(self: LoginKeycardConvertedToRegularAccountState, controller: Controller) =
if self.flowType == FlowType.LostKeycardConvertToRegularAccount:
info "restart the app because of successfully converted keycard account to regular account"
quit() # quit the app
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ proc delete*(self: LoginKeycardPinVerifiedState) =

method executePrimaryCommand*(self: LoginKeycardPinVerifiedState, controller: Controller) =
if self.flowType == FlowType.AppLogin:
controller.loginAccountKeycard()
let storeToKeychainValue = singletonInstance.localAccountSettings.getStoreToKeychainValue()
controller.loginAccountKeycard(storeToKeychainValue)
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ method executePrimaryCommand*(self: LostKeycardOptionsState, controller: Control
self.setFlowType(FlowType.LostKeycardReplacement)
controller.runLoadAccountFlow()

method executeSecondaryCommand*(self: LostKeycardOptionsState, controller: Controller) =
echo "TODO: start using account without keycard..."
method getNextSecondaryState*(self: LostKeycardOptionsState, controller: Controller): State =
if controller.isSelectedAccountAKeycardAccount():
return createState(StateType.UserProfileEnterSeedPhrase, FlowType.LostKeycardConvertToRegularAccount, self)

method resolveKeycardNextState*(self: LostKeycardOptionsState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
Expand Down
3 changes: 3 additions & 0 deletions src/app/modules/startup/internal/state.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type FlowType* {.pure.} = enum
FirstRunOldUserImportSeedPhrase = "FirstRunOldUserImportSeedPhrase"
AppLogin = "AppLogin"
LostKeycardReplacement = "LostKeycardReplacement"
LostKeycardConvertToRegularAccount = "LostKeycardConvertToRegularAccount"

type StateType* {.pure.} = enum
NoState = "NoState"
Expand All @@ -29,6 +30,7 @@ type StateType* {.pure.} = enum
UserProfileConfirmPassword = "UserProfileConfirmPassword"
UserProfileImportSeedPhrase = "UserProfileImportSeedPhrase"
UserProfileEnterSeedPhrase = "UserProfileEnterSeedPhrase"
UserProfileWrongSeedPhrase = "UserProfileWrongSeedPhrase"
Biometrics = "Biometrics"
KeycardPluginReader = "KeycardPluginReader"
KeycardInsertKeycard = "KeycardInsertKeycard"
Expand Down Expand Up @@ -69,6 +71,7 @@ type StateType* {.pure.} = enum
LoginKeycardMaxPairingSlotsReached = "LoginKeycardMaxPairingSlotsReached"
LoginKeycardEmpty = "LoginKeycardEmpty"
LoginNotKeycard = "LoginNotKeycard"
LoginKeycardConvertedToRegularAccount = "LoginKeycardConvertedToRegularAccount"
ProfileFetching = "ProfileFetching"
ProfileFetchingSuccess = "ProfileFetchingSuccess"
ProfileFetchingTimeout = "ProfileFetchingTimeout"
Expand Down
3 changes: 3 additions & 0 deletions src/app/modules/startup/internal/state_factory.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import sequtils, sugar, chronicles
import ../../../global/global_singleton
import ../../../../constants as main_constants
import ../../../../app_service/service/keycard/constants
import ../controller
Expand Down Expand Up @@ -54,12 +55,14 @@ include user_profile_create_password_state
include user_profile_create_state
include user_profile_create_same_chat_key_state
include user_profile_enter_seed_phrase_state
include user_profile_wrong_seed_phrase_state
include user_profile_import_seed_phrase_state
include welcome_state_new_user
include welcome_state_old_user
include welcome_state
include login_state
include login_plugin_state
include login_keycard_converted_to_regular_account_state
include login_keycard_insert_keycard_state
include login_keycard_inserted_keycard_state
include login_keycard_reading_keycard_state
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newUserProfileImportSeedPhraseState(flowType, backState)
if stateToBeCreated == StateType.UserProfileEnterSeedPhrase:
return newUserProfileEnterSeedPhraseState(flowType, backState)
if stateToBeCreated == StateType.UserProfileWrongSeedPhrase:
return newUserProfileWrongSeedPhraseState(flowType, backState)
if stateToBeCreated == StateType.Biometrics:
return newBiometricsState(flowType, backState)
if stateToBeCreated == StateType.KeycardPluginReader:
Expand Down Expand Up @@ -73,6 +75,8 @@ proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: St
return newLoginState(flowType, backState)
if stateToBeCreated == StateType.LoginPlugin:
return newLoginPluginState(flowType, backState)
if stateToBeCreated == StateType.LoginKeycardConvertedToRegularAccount:
return newLoginKeycardConvertedToRegularAccountState(flowType, backState)
if stateToBeCreated == StateType.LoginKeycardInsertKeycard:
return newLoginKeycardInsertKeycardState(flowType, backState)
if stateToBeCreated == StateType.LoginKeycardInsertedKeycard:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ method executePrimaryCommand*(self: UserProfileConfirmPasswordState, controller:
elif self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
controller.storeKeycardAccountAndLogin(storeToKeychain, newKeycard = true)
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
controller.storeImportedAccountAndLogin(storeToKeychain = false)
controller.storeImportedAccountAndLogin(storeToKeychain)
elif self.flowType == FlowType.LostKeycardConvertToRegularAccount:
controller.loginAccountKeycardUsingSeedPhrase(storeToKeychain)


Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ method getNextPrimaryState*(self: UserProfileEnterSeedPhraseState, controller: C
if self.enteredMnemonicMatchTargetedKeyUid:
return createState(StateType.KeycardCreatePin, self.flowType, self)
return createState(StateType.KeycardWrongKeycard, self.flowType, self)
if self.flowType == FlowType.LostKeycardReplacement:
if self.enteredMnemonicMatchTargetedKeyUid:
return createState(StateType.KeycardCreatePin, self.flowType, self)
return createState(StateType.UserProfileWrongSeedPhrase, self.flowType, self)
if self.flowType == FlowType.LostKeycardConvertToRegularAccount:
if self.enteredMnemonicMatchTargetedKeyUid:
return createState(StateType.UserProfileCreatePassword, self.flowType, self)
return createState(StateType.UserProfileWrongSeedPhrase, self.flowType, self)

method executePrimaryCommand*(self: UserProfileEnterSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.FirstRunNewUserImportSeedPhrase or
Expand Down Expand Up @@ -59,6 +67,10 @@ method executePrimaryCommand*(self: UserProfileEnterSeedPhraseState, controller:
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
else:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))
if self.flowType == FlowType.LostKeycardConvertToRegularAccount:
self.enteredMnemonicMatchTargetedKeyUid = controller.keyUidMatchSelectedLoginAccount(keyUid)
if not self.enteredMnemonicMatchTargetedKeyUid:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = true))

method resolveKeycardNextState*(self: UserProfileEnterSeedPhraseState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
type
UserProfileWrongSeedPhraseState* = ref object of State

proc newUserProfileWrongSeedPhraseState*(flowType: FlowType, backState: State): UserProfileWrongSeedPhraseState =
result = UserProfileWrongSeedPhraseState()
result.setup(flowType, StateType.UserProfileWrongSeedPhrase, backState)

proc delete*(self: UserProfileWrongSeedPhraseState) =
self.State.delete

method executeBackCommand*(self: UserProfileWrongSeedPhraseState, controller: Controller) =
if self.flowType == FlowType.LostKeycardReplacement or
self.flowType == FlowType.LostKeycardConvertToRegularAccount:
controller.setKeycardData(updatePredefinedKeycardData(controller.getKeycardData(), PredefinedKeycardData.WrongSeedPhrase, add = false))

method executePrimaryCommand*(self: UserProfileWrongSeedPhraseState, controller: Controller) =
self.executeBackCommand(controller)

method getNextPrimaryState*(self: UserProfileWrongSeedPhraseState, controller: Controller): State =
if self.flowType == FlowType.LostKeycardReplacement or
self.flowType == FlowType.LostKeycardConvertToRegularAccount:
return self.getBackState()
12 changes: 8 additions & 4 deletions src/app/modules/startup/io_interface.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ from ../../../app_service/service/keycard/service import KeycardEvent, KeyDetail

const UNIQUE_STARTUP_MODULE_IDENTIFIER* = "SartupModule"

type
StartupErrorType* {.pure.} = enum
UnknownType = 0
ImportAccError
SetupAccError
ConvertToRegularAccError

type
AccessInterface* {.pure inheritable.} = ref object of RootObj

Expand Down Expand Up @@ -92,15 +99,12 @@ method getPin*(self: AccessInterface): string {.base.} =
method getPasswordStrengthScore*(self: AccessInterface, password: string, userName: string): int {.base.} =
raise newException(ValueError, "No implementation available")

method setupAccountError*(self: AccessInterface, error: string) {.base.} =
method emitStartupError*(self: AccessInterface, error: string, errType: StartupErrorType) {.base.} =
raise newException(ValueError, "No implementation available")

method validMnemonic*(self: AccessInterface, mnemonic: string): bool {.base.} =
raise newException(ValueError, "No implementation available")

method importAccountError*(self: AccessInterface, error: string) {.base.} =
raise newException(ValueError, "No implementation available")

method importAccountSuccess*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

Expand Down
Loading

0 comments on commit 841a37e

Please sign in to comment.