Clear registration completion status if ACI changes during registration.

This commit is contained in:
Greyson Parrelli
2026-09-30 13:34:03 -03:00
committed by Alex Hart
parent aed11c8a1d
commit b95caff8f0
3 changed files with 86 additions and 0 deletions
@@ -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()
@@ -295,6 +295,51 @@ class AppRegistrationStorageControllerTest {
assertThat(SignalStore.account.isRegistered).isTrue()
}
@Test
fun `commit - different account than a completed registration - clears registration complete`() = runBlocking<Unit> {
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<Unit> {
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<Unit> {
seedInProgressData(
@@ -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()