diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageController.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageController.kt index 3b988d31da..f8d0238b7c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageController.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageController.kt @@ -750,6 +750,11 @@ class AppRegistrationStorageController(private val context: Context) : StorageCo val e164 = accountData.e164?.nullIfBlank() val isAciChanged = SignalStore.account.aci != aci + if (isAciChanged && SignalStore.registration.isRegistrationComplete) { + Log.i(TAG, "[applyAccountData] Registering a different account than the one this device completed registration for. Clearing registration-complete so the post-registration steps run again.") + SignalStore.registration.clearRegistrationComplete() + } + if (pni == null) { Log.i(TAG, "[applyAccountData] No PNI in the account data. Registering an account with no phone number. Clearing any E164/PNI state from a previous registration.") SignalStore.account.clearE164AndPni() diff --git a/app/src/test/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageControllerTest.kt b/app/src/test/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageControllerTest.kt index 7b303c16e6..bf1035c3f3 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageControllerTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageControllerTest.kt @@ -295,6 +295,51 @@ class AppRegistrationStorageControllerTest { assertThat(SignalStore.account.isRegistered).isTrue() } + @Test + fun `commit - different account than a completed registration - clears registration complete`() = runBlocking { + SignalStore.account.setAci(ACI.from(UUID.randomUUID())) + SignalStore.account.setRegistered(true) + SignalStore.registration.markRegistrationComplete() + + seedInProgressData( + RegistrationData( + accountData = accountData().newBuilder() + .e164("") + .pni("") + .pniIdentityKeyPair(ByteString.EMPTY) + .pniSignedPreKey(ByteString.EMPTY) + .pniLastResortKyberPreKey(ByteString.EMPTY) + .pniRegistrationId(0) + .build(), + accountEntropyPool = aep.value + ) + ) + + controller.commitRegistrationData() + + assertThat(SignalStore.account.aci).isEqualTo(aci) + assertThat(SignalStore.registration.isRegistrationComplete).isFalse() + } + + @Test + fun `commit - same account as a completed registration - leaves registration complete`() = runBlocking { + SignalStore.account.setAci(aci) + SignalStore.account.setRegistered(true) + SignalStore.registration.markRegistrationComplete() + + seedInProgressData( + RegistrationData( + accountData = accountData(reRegistration = true), + accountEntropyPool = aep.value, + pin = PIN + ) + ) + + controller.commitRegistrationData() + + assertThat(SignalStore.registration.isRegistrationComplete).isTrue() + } + @Test fun `commit - pin opted out - applies svr opt out`() = runBlocking { seedInProgressData( diff --git a/feature/registration/src/test/java/org/signal/registration/RegistrationEndToEndTest.kt b/feature/registration/src/test/java/org/signal/registration/RegistrationEndToEndTest.kt index dedc060688..b21e799a17 100644 --- a/feature/registration/src/test/java/org/signal/registration/RegistrationEndToEndTest.kt +++ b/feature/registration/src/test/java/org/signal/registration/RegistrationEndToEndTest.kt @@ -1701,6 +1701,42 @@ class RegistrationEndToEndTest { assert(request.pniPreKeys == null) { "An account with no phone number has no PNI, so no PNI key material should be sent" } } + @Test + fun `registering a new signal login on a device already registered to another account commits a different aci`() { + enableSignalLoginRegistration() + val preExisting = preExistingRegistrationData(E164) + storageController.preExistingRegistrationData = preExisting + + var registrationComplete = false + launchRegistrationFlow(onRegistrationComplete = { registrationComplete = true }) + + startSignalLoginRegistration() + buySignalLogin() + + waitForTag(TestTags.SIGNAL_LOGIN_INFO_SCREEN) + val login = registeredSignalLogin() + + recordSignalLoginManually() + enterSignalLogin(login) + + waitForTag(TestTags.ADD_USERNAME_FIELD) + composeTestRule.onNodeWithTag(TestTags.ADD_USERNAME_FIELD).performTextInput(USERNAME) + waitForEnabledTag(TestTags.ADD_USERNAME_NEXT_BUTTON) + composeTestRule.onNodeWithTag(TestTags.ADD_USERNAME_NEXT_BUTTON).performClick() + + waitFor("registration to complete") { registrationComplete } + + // The app detects the switch to a new account by comparing the committed ACI against the device's, and uses it to + // re-run the post-registration steps (like setting a profile name) that the previous account already completed. + val accountData = storageController.committedData?.accountData + assert(accountData != null) { "Expected account data to be committed" } + assert(accountData!!.aci == login.aci.toString()) { "Expected committed ACI ${login.aci} but was ${accountData.aci}" } + assert(accountData.aci != preExisting.aci.toString()) { "Expected a new account, but the committed ACI is the one already on the device" } + assert(!accountData.reRegistration) { "Expected a brand new account to not be flagged as a re-registration" } + assert(networkController.lastRegisterAccountRequest?.aci == null) { "Expected the device's existing ACI to not be sent but was ${networkController.lastRegisterAccountRequest?.aci}" } + assert(storageController.savedUsername == "$USERNAME.42") { "Expected the confirmed username to be saved but was ${storageController.savedUsername}" } + } + @Test fun `a purchased signal login that the password manager takes and hands back moves the user on to the username step`() { enableSignalLoginRegistration()