From 9afd63e4139bd9cfd5dee2b702e974070ba4e47a Mon Sep 17 00:00:00 2001 From: automated-signal <37887102+automated-signal@users.noreply.github.com> Date: Thu, 23 Jul 2026 15:59:30 -0500 Subject: [PATCH] Standalone Registration: Better handle incorrect verification codes Co-authored-by: Scott Nonnenberg --- package.json | 2 +- pnpm-lock.yaml | 10 ++--- ts/state/ducks/standaloneInstaller.preload.ts | 34 +++++++++-------- .../standalone/registration_test.node.ts | 38 +++++++++++++++++-- ts/textsecure/WebAPI.preload.ts | 7 +++- 5 files changed, 65 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index f13bfacbe0..d69fb29962 100644 --- a/package.json +++ b/package.json @@ -162,7 +162,7 @@ "@react-spring/web": "10.0.3", "@signalapp/lame": "workspace:*", "@signalapp/minimask": "1.0.1", - "@signalapp/mock-server": "25.0.0", + "@signalapp/mock-server": "25.0.1", "@signalapp/parchment-cjs": "3.0.1", "@signalapp/quill-cjs": "2.1.2", "@storybook/addon-a11y": "8.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a3f1a711d6..c7d99c7b44 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -153,8 +153,8 @@ importers: specifier: 1.0.1 version: 1.0.1 '@signalapp/mock-server': - specifier: 25.0.0 - version: 25.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + specifier: 25.0.1 + version: 25.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@signalapp/parchment-cjs': specifier: 3.0.1 version: 3.0.1 @@ -4476,8 +4476,8 @@ packages: '@signalapp/minimask@1.0.1': resolution: {integrity: sha512-QAwo0joA60urTNbW9RIz6vLKQjy+jdVtH7cvY0wD9PVooD46MAjE40MLssp4xUJrph91n2XvtJ3pbEUDrmT2AA==, tarball: https://registry.npmjs.org/@signalapp/minimask/-/minimask-1.0.1.tgz} - '@signalapp/mock-server@25.0.0': - resolution: {integrity: sha512-gEOYKlwppfs8XT7feg82NQKab8XOVAaQw2rAOGth60BVpzeZI0eiTF2JoNBjVdLbJamFX8Cz2CrZAyXvJhp7Jg==, tarball: https://registry.npmjs.org/@signalapp/mock-server/-/mock-server-25.0.0.tgz} + '@signalapp/mock-server@25.0.1': + resolution: {integrity: sha512-bOQ76sRp32ry7u8thgC9DAHmRuyx+P6JXHJ0g64onjrydjpcHOc0b/u5Y68HDFdvfB1LyG1FWvOO/Fg3C0f37Q==, tarball: https://registry.npmjs.org/@signalapp/mock-server/-/mock-server-25.0.1.tgz} '@signalapp/parchment-cjs@3.0.1': resolution: {integrity: sha512-hSBMQ1M7wE4GcC8ZeNtvpJF+DAJg3eIRRf1SiHS3I3Algav/sgJJNm6HIYm6muHuK7IJmuEjkL3ILSXgmu0RfQ==, tarball: https://registry.npmjs.org/@signalapp/parchment-cjs/-/parchment-cjs-3.0.1.tgz} @@ -15254,7 +15254,7 @@ snapshots: '@signalapp/minimask@1.0.1': {} - '@signalapp/mock-server@25.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@signalapp/mock-server@25.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@indutny/parallel-prettier': 3.0.0(prettier@3.8.3) '@indutny/protopiler': 4.0.0 diff --git a/ts/state/ducks/standaloneInstaller.preload.ts b/ts/state/ducks/standaloneInstaller.preload.ts index 7c2e21ad1d..1e3ce0b6d3 100644 --- a/ts/state/ducks/standaloneInstaller.preload.ts +++ b/ts/state/ducks/standaloneInstaller.preload.ts @@ -38,7 +38,10 @@ import { ValidStepsBeforeComplete, } from '../../types/StandaloneRegistration.std.ts'; import { ErrorCode, LibSignalErrorBase } from '@signalapp/libsignal-client'; -import { SessionNotAllowedToRequestCodeError } from '../../textsecure/Errors.std.ts'; +import { + SessionNotAllowedToRequestCodeError, + SessionNotVerifiedError, +} from '../../textsecure/Errors.std.ts'; import { openInbox } from './app.preload.ts'; import { PhoneNumberDiscoverability } from '../../util/phoneNumberDiscoverability.std.ts'; import { itemStorage } from '../../textsecure/Storage.preload.ts'; @@ -566,25 +569,26 @@ export function submitVerificationCode({ } catch (error) { log.error(`${logId}: error submitting code`, toLogFormat(error)); + if (error instanceof SessionNotVerifiedError) { + workflow = { + ...workflow, + failedSubmitCodeCount: workflow.failedSubmitCodeCount + 1, + status: { + type: 'failed', + error: 'incorrect-code', + }, + }; + dispatch(updateWorkflow(workflow)); + return; + } + if (error instanceof LibSignalErrorBase) { if ( error.is(ErrorCode.RegistrationRequestInvalid) || error.is(ErrorCode.RegistrationRequestRejected) || - error.is(ErrorCode.RegistrationSessionIdInvalid) + error.is(ErrorCode.RegistrationSessionIdInvalid) || + error.is(ErrorCode.RegistrationSessionNotReadyForVerification) ) { - workflow = { - ...workflow, - failedSubmitCodeCount: workflow.failedSubmitCodeCount + 1, - status: { - type: 'failed', - error: 'incorrect-code', - }, - }; - dispatch(updateWorkflow(workflow)); - return; - } - - if (error.is(ErrorCode.RegistrationSessionNotReadyForVerification)) { workflow = { ...workflow, failedSubmitCodeCount: workflow.failedSubmitCodeCount + 1, diff --git a/ts/test-mock/standalone/registration_test.node.ts b/ts/test-mock/standalone/registration_test.node.ts index 6a3fb26d20..76ac6bd47b 100644 --- a/ts/test-mock/standalone/registration_test.node.ts +++ b/ts/test-mock/standalone/registration_test.node.ts @@ -69,10 +69,40 @@ describe('registration', function (this: Mocha.Suite) { } { - debug('VERIFICATION_CODE: enter code'); - const CODE = '111111'; - for (let i = 0; i < CODE.length; i += 1) { - const char = CODE[i]; + debug('VERIFICATION_CODE: enter incorrect code'); + const INCORRECT_CODE = '123456'; + for (let i = 0; i < INCORRECT_CODE.length; i += 1) { + const char = INCORRECT_CODE[i]; + if (!char) { + continue; + } + + const codeInput = window.getByLabel(`Character ${i + 1} of 6`); + // oxlint-disable-next-line no-await-in-loop + await typeIntoInput(codeInput, char, ''); + } + + await window.getByRole('button', { name: 'Continue' }).click(); + + // Dismiss the dialog that comes up + await window.getByRole('button', { name: 'OK' }).click(); + } + + { + debug('VERIFICATION_CODE: enter correct code'); + const CORRECT_CODE = '111111'; + + // We need to delete the content from the left-most input 6 times + const firstInput = window.getByLabel(`Character 1 of 6`); + await firstInput.clear(); + await firstInput.clear(); + await firstInput.clear(); + await firstInput.clear(); + await firstInput.clear(); + await firstInput.clear(); + + for (let i = 0; i < CORRECT_CODE.length; i += 1) { + const char = CORRECT_CODE[i]; if (!char) { continue; } diff --git a/ts/textsecure/WebAPI.preload.ts b/ts/textsecure/WebAPI.preload.ts index aac02e91db..192036a487 100644 --- a/ts/textsecure/WebAPI.preload.ts +++ b/ts/textsecure/WebAPI.preload.ts @@ -2863,7 +2863,12 @@ export async function submitCodeForVerificationSession(options: { sessionId: options.verificationSessionId, }); - await session.verifySession(options.code); + const success = await session.verifySession(options.code); + if (!success) { + throw new SessionNotVerifiedError( + 'submitCodeForVerificationSession: verifySession returned false!' + ); + } // Verify that the code worked to make the session ready for account creation if (!session.sessionState.verified) {