From 35ca16f294252862856f0934987d6ecf67d855a5 Mon Sep 17 00:00:00 2001 From: andrew-signal Date: Fri, 17 Jul 2026 12:07:59 -0400 Subject: [PATCH] Migrate to libsignal deleteUsernameHash. Co-authored-by: Cody Henthorne --- .../profiles/manage/UsernameRepository.kt | 13 +++++++++---- .../signalservice/api/account/AccountApi.kt | 16 +++++++++++----- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt index 46e510e843..d10eea236e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt @@ -536,8 +536,8 @@ object UsernameRepository { return UsernameDeleteResult.NETWORK_ERROR } - return when (val result = SignalNetwork.account.deleteUsername()) { - is NetworkResult.Success -> { + return when (val result = SignalNetwork.account.deleteUsernameHash()) { + is RequestResult.Success -> { SignalDatabase.recipients.setUsername(Recipient.self().id, null) SignalStore.account.username = null SignalStore.account.usernameLink = null @@ -553,10 +553,15 @@ object UsernameRepository { Log.i(TAG, "[deleteUsername] Successfully deleted the username.") UsernameDeleteResult.SUCCESS } - else -> { - Log.w(TAG, "[deleteUsername] Generic network exception.", result.getCause()) + + is RequestResult.RetryableNetworkError -> { + Log.w(TAG, "[deleteUsername] Generic network exception.", result.networkError) UsernameDeleteResult.NETWORK_ERROR } + + is RequestResult.ApplicationError -> throw result.cause + + is RequestResult.NonSuccess -> error("Code branch is unreachable") } } diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/AccountApi.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/AccountApi.kt index d8e1a35687..5db3a1f515 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/AccountApi.kt +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/AccountApi.kt @@ -9,6 +9,7 @@ import kotlinx.coroutines.runBlocking import org.signal.core.util.Base64 import org.signal.core.util.Base64.encodeUrlSafeWithoutPadding import org.signal.libsignal.net.AuthDevicesService +import org.signal.libsignal.net.AuthUsernamesService import org.signal.libsignal.net.RequestResult import org.signal.libsignal.usernames.BaseUsernameException import org.signal.libsignal.usernames.Username @@ -215,12 +216,17 @@ class AccountApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSock } /** - * DELETE /v1/accounts/username_hash - * - 204: Success + * Clears the current username hash, ciphertext, and link for the authenticated account. + * + * This also succeeds if the account has no username set, so a caller retrying a deletion sees + * the same result as the original call. */ - fun deleteUsername(): NetworkResult { - val request = WebSocketRequestMessage.delete("/v1/accounts/username_hash") - return NetworkResult.fromWebSocketRequest(authWebSocket, request) + fun deleteUsernameHash(): RequestResult { + return runBlocking { + authWebSocket.runCatchingWithChatConnection { connection -> + AuthUsernamesService(connection).deleteUsernameHash() + } + } } /**