diff --git a/app/src/providers/authProvider.tsx b/app/src/providers/authProvider.tsx index 9b165d1a5..207297415 100644 --- a/app/src/providers/authProvider.tsx +++ b/app/src/providers/authProvider.tsx @@ -369,6 +369,11 @@ export async function getOrGeneratePointsAddress( return pointsAddr; } +export function getPrivateKeyFromMnemonic(mnemonic: string) { + const wallet = ethers.HDNodeWallet.fromPhrase(mnemonic); + return wallet.privateKey; +} + export async function hasSecretStored() { const seed = await Keychain.getGenericPassword({ service: SERVICE_NAME }); return !!seed; @@ -470,8 +475,7 @@ export async function unsafe_getPrivateKey(keychainOptions?: KeychainOptions) { return null; } const mnemonic = JSON.parse(foundMnemonic) as Mnemonic; - const wallet = ethers.HDNodeWallet.fromPhrase(mnemonic.phrase); - return wallet.privateKey; + return getPrivateKeyFromMnemonic(mnemonic.phrase); } export const useAuth = () => { diff --git a/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx b/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx index 29b5a9a7f..7498f2951 100644 --- a/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx +++ b/app/src/screens/account/recovery/AccountRecoveryChoiceScreen.tsx @@ -32,9 +32,9 @@ import RestoreAccountSvg from '@/assets/icons/restore_account.svg'; import useHapticNavigation from '@/hooks/useHapticNavigation'; import { ExpandableBottomLayout } from '@/layouts/ExpandableBottomLayout'; import type { RootStackParamList } from '@/navigation'; -import { useAuth } from '@/providers/authProvider'; +import { getPrivateKeyFromMnemonic, useAuth } from '@/providers/authProvider'; import { - loadPassportDataAndSecret, + loadPassportData, reStorePassportDataWithRightCSCA, } from '@/providers/passportDataProvider'; import { STORAGE_NAME, useBackupMnemonic } from '@/services/cloud-backup'; @@ -85,32 +85,55 @@ const AccountRecoveryChoiceScreen: React.FC = () => { return false; } - const passportDataAndSecret = - (await loadPassportDataAndSecret()) as string; - const { passportData, secret } = JSON.parse(passportDataAndSecret); + const passportData = await loadPassportData(); + const secret = getPrivateKeyFromMnemonic(mnemonic.phrase); + + if (!passportData || !secret) { + console.warn('Failed to load passport data or secret'); + trackEvent(BackupEvents.CLOUD_RESTORE_FAILED_AUTH, { + reason: 'no_passport_data_or_secret', + }); + navigation.navigate({ name: 'Home', params: {} }); + setRestoring(false); + return false; + } + + const passportDataParsed = JSON.parse(passportData); + const { isRegistered, csca } = - await isUserRegisteredWithAlternativeCSCA(passportData, secret, { - getCommitmentTree(docCategory) { - return useProtocolStore.getState()[docCategory].commitment_tree; - }, - getAltCSCA(docCategory) { - if (docCategory === 'aadhaar') { - const publicKeys = - useProtocolStore.getState().aadhaar.public_keys; - // Convert string[] to Record format expected by AlternativeCSCA - return publicKeys - ? Object.fromEntries(publicKeys.map(key => [key, key])) - : {}; - } + await isUserRegisteredWithAlternativeCSCA( + passportDataParsed, + secret as string, + { + getCommitmentTree(docCategory) { + return useProtocolStore.getState()[docCategory].commitment_tree; + }, + getAltCSCA(docCategory) { + if (docCategory === 'aadhaar') { + const publicKeys = + useProtocolStore.getState().aadhaar.public_keys; + // Convert string[] to Record format expected by AlternativeCSCA + return publicKeys + ? Object.fromEntries(publicKeys.map(key => [key, key])) + : {}; + } - return useProtocolStore.getState()[docCategory].alternative_csca; + return useProtocolStore.getState()[docCategory] + .alternative_csca; + }, }, - }); + ); if (!isRegistered) { console.warn( 'Secret provided did not match a registered ID. Please try again.', ); - trackEvent(BackupEvents.CLOUD_RESTORE_FAILED_PASSPORT_NOT_REGISTERED); + trackEvent( + BackupEvents.CLOUD_RESTORE_FAILED_PASSPORT_NOT_REGISTERED, + { + reason: 'document_not_registered', + hasCSCA: !!csca, + }, + ); navigation.navigate({ name: 'Home', params: {} }); setRestoring(false); return false; @@ -118,7 +141,10 @@ const AccountRecoveryChoiceScreen: React.FC = () => { if (isCloudRestore && !cloudBackupEnabled) { toggleCloudBackupEnabled(); } - reStorePassportDataWithRightCSCA(passportData, csca as string); + await reStorePassportDataWithRightCSCA( + passportDataParsed, + csca as string, + ); await markCurrentDocumentAsRegistered(selfClient); trackEvent(BackupEvents.CLOUD_RESTORE_SUCCESS); trackEvent(BackupEvents.ACCOUNT_RECOVERY_COMPLETED); diff --git a/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx b/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx index e82a056d8..3f3e5e638 100644 --- a/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx +++ b/app/src/screens/account/recovery/RecoverWithPhraseScreen.tsx @@ -31,9 +31,9 @@ import { import Paste from '@/assets/icons/paste.svg'; import type { RootStackParamList } from '@/navigation'; -import { useAuth } from '@/providers/authProvider'; +import { getPrivateKeyFromMnemonic, useAuth } from '@/providers/authProvider'; import { - loadPassportDataAndSecret, + loadPassportData, reStorePassportDataWithRightCSCA, } from '@/providers/passportDataProvider'; @@ -74,8 +74,10 @@ const RecoverWithPhraseScreen: React.FC = () => { return; } - const passportDataAndSecret = await loadPassportDataAndSecret(); - if (!passportDataAndSecret) { + const passportData = await loadPassportData(); + const secret = getPrivateKeyFromMnemonic(slimMnemonic); + + if (!passportData || !secret) { console.warn( 'No passport data found on device. Please scan or import your document.', ); @@ -86,9 +88,10 @@ const RecoverWithPhraseScreen: React.FC = () => { setRestoring(false); return; } - const { passportData, secret } = JSON.parse(passportDataAndSecret); + const passportDataParsed = JSON.parse(passportData); + const { isRegistered, csca } = await isUserRegisteredWithAlternativeCSCA( - passportData, + passportDataParsed, secret as string, { getCommitmentTree(docCategory) { @@ -122,7 +125,7 @@ const RecoverWithPhraseScreen: React.FC = () => { } if (csca) { - await reStorePassportDataWithRightCSCA(passportData, csca); + await reStorePassportDataWithRightCSCA(passportDataParsed, csca); } await markCurrentDocumentAsRegistered(selfClient);