From e4f030d0e3fb5cfa466d4ef7274e6c59896eeffe Mon Sep 17 00:00:00 2001 From: David Perez Date: Wed, 13 May 2026 15:05:53 -0500 Subject: [PATCH] Bug: Support translations for Cookie Acquisition error (#6917) --- .../manager/di/PlatformManagerModule.kt | 3 ++ .../network/NetworkCookieManagerImpl.kt | 9 ++++++ .../platform/glide/GlideCookieInterceptor.kt | 1 + .../network/NetworkCookieManagerTest.kt | 21 ++++++++++++++ .../platform/util/ThrowableExtensionsTest.kt | 10 +++++-- .../data/vault/manager/CipherManagerTest.kt | 28 +++++++++++-------- .../data/vault/manager/SendManagerTest.kt | 6 ++-- .../glide/GlideCookieInterceptorTest.kt | 2 ++ .../vault/feature/vault/VaultViewModelTest.kt | 13 +++++---- .../network/di/PlatformNetworkModule.kt | 2 ++ .../exception/CookieRedirectException.kt | 5 ++-- .../network/interceptor/CookieInterceptor.kt | 6 +++- .../network/provider/CookieProvider.kt | 5 ++++ .../interceptor/CookieInterceptorTest.kt | 3 ++ .../network/model/BitwardenErrorTest.kt | 8 ++++-- .../network/util/ExceptionExtensionsTest.kt | 2 +- ui/src/main/res/values/strings.xml | 1 + 17 files changed, 94 insertions(+), 31 deletions(-) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt index 0a1c615873..b551e5b11a 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt @@ -89,6 +89,7 @@ import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource import com.x8bit.bitwarden.data.vault.manager.VaultLockManager import com.x8bit.bitwarden.data.vault.repository.VaultRepository +import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -439,10 +440,12 @@ object PlatformManagerModule { @Provides @Singleton fun provideNetworkCookieManager( + resourceManager: ResourceManager, configDiskSource: ConfigDiskSource, cookieDiskSource: CookieDiskSource, cookieAcquisitionRequestManager: CookieAcquisitionRequestManager, ): NetworkCookieManager = NetworkCookieManagerImpl( + resourceManager = resourceManager, configDiskSource = configDiskSource, cookieDiskSource = cookieDiskSource, cookieAcquisitionRequestManager = cookieAcquisitionRequestManager, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkCookieManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkCookieManagerImpl.kt index 0ccdfa6af0..9828f71a71 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkCookieManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkCookieManagerImpl.kt @@ -2,11 +2,13 @@ package com.x8bit.bitwarden.data.platform.manager.network import com.bitwarden.data.datasource.disk.ConfigDiskSource import com.bitwarden.network.model.NetworkCookie +import com.bitwarden.ui.platform.resource.BitwardenString import com.x8bit.bitwarden.data.platform.datasource.disk.CookieDiskSource import com.x8bit.bitwarden.data.platform.datasource.disk.model.CookieConfigurationData import com.x8bit.bitwarden.data.platform.manager.CookieAcquisitionRequestManager import com.x8bit.bitwarden.data.platform.manager.model.CookieAcquisitionRequest import com.x8bit.bitwarden.data.platform.manager.util.toNetworkCookieList +import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager import timber.log.Timber private const val BOOTSTRAP_TYPE_SSO_COOKIE_VENDOR = "ssoCookieVendor" @@ -15,6 +17,7 @@ private const val BOOTSTRAP_TYPE_SSO_COOKIE_VENDOR = "ssoCookieVendor" * Default implementation of [NetworkCookieManager]. */ class NetworkCookieManagerImpl( + private val resourceManager: ResourceManager, private val configDiskSource: ConfigDiskSource, private val cookieDiskSource: CookieDiskSource, private val cookieAcquisitionRequestManager: CookieAcquisitionRequestManager, @@ -32,6 +35,12 @@ class NetworkCookieManagerImpl( ?.takeIf { it.type == BOOTSTRAP_TYPE_SSO_COOKIE_VENDOR } ?.cookieDomain + override val errorMessageString: String + get() = resourceManager.getString( + resId = BitwardenString + .your_request_was_interrupted_because_the_app_needed_to_reauthenticate, + ) + override fun needsBootstrap(hostname: String): Boolean { val result = configDiskSource .serverConfig diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/glide/GlideCookieInterceptor.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/glide/GlideCookieInterceptor.kt index 0211fbbc96..bbe0b8d163 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/glide/GlideCookieInterceptor.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/glide/GlideCookieInterceptor.kt @@ -43,6 +43,7 @@ class GlideCookieInterceptor( response.close() throw CookieRedirectException( hostname = response.request.url.host, + message = cookieProvider.errorMessageString, ) } } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkCookieManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkCookieManagerTest.kt index 3e7d6e13bd..400488d89b 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkCookieManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/manager/network/NetworkCookieManagerTest.kt @@ -4,10 +4,12 @@ import com.bitwarden.data.datasource.disk.model.ServerConfig import com.bitwarden.data.datasource.disk.util.FakeConfigDiskSource import com.bitwarden.network.model.ConfigResponseJson import com.bitwarden.network.model.NetworkCookie +import com.bitwarden.ui.platform.resource.BitwardenString import com.x8bit.bitwarden.data.platform.datasource.disk.CookieDiskSource import com.x8bit.bitwarden.data.platform.datasource.disk.model.CookieConfigurationData import com.x8bit.bitwarden.data.platform.manager.CookieAcquisitionRequestManager import com.x8bit.bitwarden.data.platform.manager.model.CookieAcquisitionRequest +import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager import io.mockk.every import io.mockk.just import io.mockk.mockk @@ -20,6 +22,9 @@ import org.junit.jupiter.api.Test class NetworkCookieManagerTest { + private val resourceManager: ResourceManager = mockk { + every { getString(resId = any()) } returns ERROR_MESSAGE + } private val fakeConfigDiskSource = FakeConfigDiskSource() private val mockCookieDiskSource: CookieDiskSource = mockk() private val mockCookieAcquisitionRequestManager: CookieAcquisitionRequestManager = @@ -28,11 +33,25 @@ class NetworkCookieManagerTest { } private val manager = NetworkCookieManagerImpl( + resourceManager = resourceManager, configDiskSource = fakeConfigDiskSource, cookieDiskSource = mockCookieDiskSource, cookieAcquisitionRequestManager = mockCookieAcquisitionRequestManager, ) + @Test + fun `errorMessageString should return appropriate message`() { + val result = manager.errorMessageString + + assertEquals(ERROR_MESSAGE, result) + verify(exactly = 1) { + resourceManager.getString( + resId = BitwardenString + .your_request_was_interrupted_because_the_app_needed_to_reauthenticate, + ) + } + } + @Test fun `needsBootstrap should return false when serverConfig is null`() { fakeConfigDiskSource.serverConfig = null @@ -284,3 +303,5 @@ private fun createCookieConfig( hostname = hostname, cookies = cookies, ) + +private const val ERROR_MESSAGE: String = "Error Message" diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/util/ThrowableExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/util/ThrowableExtensionsTest.kt index b8f983109f..1f85fec29a 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/util/ThrowableExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/util/ThrowableExtensionsTest.kt @@ -10,10 +10,14 @@ class ThrowableExtensionsTest { @Test fun `userFriendlyMessage should return message for CookieRedirectException`() { - val exception = CookieRedirectException(hostname = "example.com") + val message = "Your request was interrupted because the app needed to " + + "re-authenticate. Please try again." + val exception = CookieRedirectException( + hostname = "example.com", + message = message, + ) assertEquals( - "Your request was interrupted because the app needed to " + - "re-authenticate. Please try again.", + message, exception.userFriendlyMessage, ) } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt index 9191c5429b..343d1182d7 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt @@ -2941,7 +2941,9 @@ class CipherManagerTest { runTest { fakeAuthDiskSource.userState = MOCK_USER_STATE val cipherId = "mockId-1" - val error = CookieRedirectException("test.host") + val message = "Your request was interrupted because " + + "the app needed to re-authenticate. Please try again." + val error = CookieRedirectException(hostname = "test.host", message = message) coEvery { ciphersService.hardDeleteCipher(cipherId = cipherId) } returns error.asFailure() @@ -2950,8 +2952,7 @@ class CipherManagerTest { assertEquals( DeleteCipherResult.Error( - errorMessage = "Your request was interrupted because " + - "the app needed to re-authenticate. Please try again.", + errorMessage = message, error = error, ), result, @@ -2967,7 +2968,9 @@ class CipherManagerTest { val cipherId = "mockId-1" val cipherView = createMockCipherView(number = 1) val encryptionContext = createMockEncryptionContext(number = 1) - val error = CookieRedirectException("test.host") + val message = "Your request was interrupted because " + + "the app needed to re-authenticate. Please try again." + val error = CookieRedirectException(hostname = "test.host", message = message) coEvery { vaultSdkSource.encryptCipher(userId = userId, cipherView = cipherView) } returns encryptionContext.asSuccess() @@ -2982,8 +2985,7 @@ class CipherManagerTest { assertEquals( DeleteCipherResult.Error( - errorMessage = "Your request was interrupted because " + - "the app needed to re-authenticate. Please try again.", + errorMessage = message, error = error, ), result, @@ -2997,7 +2999,9 @@ class CipherManagerTest { fakeAuthDiskSource.userState = MOCK_USER_STATE val cipherId = "mockId-1" val cipherView = createMockCipherView(number = 1) - val error = CookieRedirectException("test.host") + val message = "Your request was interrupted because " + + "the app needed to re-authenticate. Please try again." + val error = CookieRedirectException(hostname = "test.host", message = message) coEvery { ciphersService.restoreCipher(cipherId = cipherId) } returns error.asFailure() @@ -3009,8 +3013,7 @@ class CipherManagerTest { assertEquals( RestoreCipherResult.Error( - errorMessage = "Your request was interrupted because " + - "the app needed to re-authenticate. Please try again.", + errorMessage = message, error = error, ), result, @@ -3023,7 +3026,9 @@ class CipherManagerTest { runTest { fakeAuthDiskSource.userState = MOCK_USER_STATE val cipherId = "mockId-1" - val error = CookieRedirectException("test.host") + val message = "Your request was interrupted because " + + "the app needed to re-authenticate. Please try again." + val error = CookieRedirectException(hostname = "test.host", message = message) coEvery { ciphersService.archiveCipher(cipherId = cipherId) } returns error.asFailure() @@ -3035,8 +3040,7 @@ class CipherManagerTest { assertEquals( ArchiveCipherResult.Error( - errorMessage = "Your request was interrupted because " + - "the app needed to re-authenticate. Please try again.", + errorMessage = message, error = error, ), result, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt index 9cb821cb1f..e2693a7821 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt @@ -686,7 +686,9 @@ class SendManagerTest { runTest { fakeAuthDiskSource.userState = MOCK_USER_STATE val sendId = "mockId-1" - val error = CookieRedirectException(hostname = "example.com") + val message = "Your request was interrupted because " + + "the app needed to re-authenticate. Please try again." + val error = CookieRedirectException(hostname = "test.host", message = message) coEvery { sendsService.deleteSend(sendId = sendId) } returns error.asFailure() @@ -695,7 +697,7 @@ class SendManagerTest { assertEquals( DeleteSendResult.Error( - errorMessage = error.message, + errorMessage = message, error = error, ), result, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/glide/GlideCookieInterceptorTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/glide/GlideCookieInterceptorTest.kt index 034c50fd96..6c2767bfcc 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/glide/GlideCookieInterceptorTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/glide/GlideCookieInterceptorTest.kt @@ -102,6 +102,7 @@ class GlideCookieInterceptorTest { every { mockCookieProvider.getCookies("vault.bitwarden.com") } returns emptyList() + every { mockCookieProvider.errorMessageString } returns "Error" val exception = assertThrows { interceptor.intercept(chain) @@ -134,6 +135,7 @@ class GlideCookieInterceptorTest { } returns listOf( NetworkCookie(name = "awselb", value = "session123"), ) + every { mockCookieProvider.errorMessageString } returns "Error" val exception = assertThrows { interceptor.intercept(chain) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt index b43cea7322..ed9a598ecd 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt @@ -1760,9 +1760,11 @@ class VaultViewModelTest : BaseViewModelTest() { @Test fun `vaultDataStateFlow Error with CookieRedirectException should show user-friendly message`() = runTest { + val message = "Your request was interrupted because " + + "the app needed to re-authenticate. Please try again." mutableVaultDataStateFlow.tryEmit( value = DataState.Error( - error = CookieRedirectException(hostname = "example.com"), + error = CookieRedirectException(hostname = "example.com", message = message), ), ) @@ -1771,10 +1773,7 @@ class VaultViewModelTest : BaseViewModelTest() { assertEquals( createMockVaultState( viewState = VaultState.ViewState.Error( - message = ( - "Your request was interrupted because the app needed to " + - "re-authenticate. Please try again." - ).asText(), + message = message.asText(), ), ), viewModel.stateFlow.value, @@ -1856,9 +1855,11 @@ class VaultViewModelTest : BaseViewModelTest() { @Test fun `vaultDataStateFlow Error with CookieRedirectException with items should show user-friendly error dialog`() = runTest { + val message = "Your request was interrupted because " + + "the app needed to re-authenticate. Please try again." mutableVaultDataStateFlow.tryEmit( value = DataState.Error( - error = CookieRedirectException(hostname = "example.com"), + error = CookieRedirectException(hostname = "example.com", message = message), data = VaultData( decryptCipherListResult = createMockDecryptCipherListResult( number = 1, diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/network/di/PlatformNetworkModule.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/network/di/PlatformNetworkModule.kt index 6ced3891e6..fc8ffb920c 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/network/di/PlatformNetworkModule.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/data/platform/datasource/network/di/PlatformNetworkModule.kt @@ -75,6 +75,8 @@ object PlatformNetworkModule { override fun getPrivateKey(alias: String?): PrivateKey? = null }, cookieProvider = object : CookieProvider { + override val errorMessageString: String get() = "Error" + override fun needsBootstrap(hostname: String): Boolean = false override fun getCookies(hostname: String): List = emptyList() diff --git a/network/src/main/kotlin/com/bitwarden/network/exception/CookieRedirectException.kt b/network/src/main/kotlin/com/bitwarden/network/exception/CookieRedirectException.kt index ba486961e9..fd38955959 100644 --- a/network/src/main/kotlin/com/bitwarden/network/exception/CookieRedirectException.kt +++ b/network/src/main/kotlin/com/bitwarden/network/exception/CookieRedirectException.kt @@ -13,6 +13,5 @@ import java.io.IOException */ class CookieRedirectException( val hostname: String, -) : IOException( - "Your request was interrupted because the app needed to re-authenticate. Please try again.", -) + message: String, +) : IOException(message) diff --git a/network/src/main/kotlin/com/bitwarden/network/interceptor/CookieInterceptor.kt b/network/src/main/kotlin/com/bitwarden/network/interceptor/CookieInterceptor.kt index cae17f671b..866da2c510 100644 --- a/network/src/main/kotlin/com/bitwarden/network/interceptor/CookieInterceptor.kt +++ b/network/src/main/kotlin/com/bitwarden/network/interceptor/CookieInterceptor.kt @@ -60,7 +60,10 @@ internal class CookieInterceptor( if (cookieProvider.needsBootstrap(hostname)) { Timber.d("Cookie bootstrap required for $hostname, triggering acquisition") cookieProvider.acquireCookies(hostname) - throw CookieRedirectException(hostname = hostname) + throw CookieRedirectException( + hostname = hostname, + message = cookieProvider.errorMessageString, + ) } val request = originalRequest.withCookies(hostname) @@ -77,6 +80,7 @@ internal class CookieInterceptor( cookieProvider.acquireCookies(hostname) throw CookieRedirectException( hostname = hostname, + message = cookieProvider.errorMessageString, ) } diff --git a/network/src/main/kotlin/com/bitwarden/network/provider/CookieProvider.kt b/network/src/main/kotlin/com/bitwarden/network/provider/CookieProvider.kt index 3ef5910ba2..f30b148d2f 100644 --- a/network/src/main/kotlin/com/bitwarden/network/provider/CookieProvider.kt +++ b/network/src/main/kotlin/com/bitwarden/network/provider/CookieProvider.kt @@ -10,6 +10,11 @@ import com.bitwarden.network.model.NetworkCookie * Bitwarden API endpoints behind a load balancer. */ interface CookieProvider { + /** + * The translated human-readable string to be displayed when the missing cookie is the reason + * for a request failure. + */ + val errorMessageString: String /** * Determines if the given [hostname] requires cookie bootstrap. diff --git a/network/src/test/kotlin/com/bitwarden/network/interceptor/CookieInterceptorTest.kt b/network/src/test/kotlin/com/bitwarden/network/interceptor/CookieInterceptorTest.kt index 51c2c5385c..eecf7709b3 100644 --- a/network/src/test/kotlin/com/bitwarden/network/interceptor/CookieInterceptorTest.kt +++ b/network/src/test/kotlin/com/bitwarden/network/interceptor/CookieInterceptorTest.kt @@ -66,6 +66,7 @@ class CookieInterceptorTest { val chain = FakeInterceptorChain(originalRequest) every { mockCookieProvider.needsBootstrap("vault.bitwarden.com") } returns true + every { mockCookieProvider.errorMessageString } returns "Error" val exception = assertThrows { interceptor.intercept(chain) @@ -159,6 +160,7 @@ class CookieInterceptorTest { every { mockCookieProvider.needsBootstrap("vault.bitwarden.com") } returns false every { mockCookieProvider.getCookies("vault.bitwarden.com") } returns emptyList() + every { mockCookieProvider.errorMessageString } returns "Error" val exception = assertThrows { interceptor.intercept(chain) @@ -224,6 +226,7 @@ class CookieInterceptorTest { every { mockCookieProvider.needsBootstrap("vault.bitwarden.com") } returns false every { mockCookieProvider.getCookies("vault.bitwarden.com") } returns cookies + every { mockCookieProvider.errorMessageString } returns "Error" val exception = assertThrows { interceptor.intercept(chain) diff --git a/network/src/test/kotlin/com/bitwarden/network/model/BitwardenErrorTest.kt b/network/src/test/kotlin/com/bitwarden/network/model/BitwardenErrorTest.kt index 9f51372e5a..8ac97ae898 100644 --- a/network/src/test/kotlin/com/bitwarden/network/model/BitwardenErrorTest.kt +++ b/network/src/test/kotlin/com/bitwarden/network/model/BitwardenErrorTest.kt @@ -13,7 +13,7 @@ class BitwardenErrorTest { @Test fun `toBitwardenError with CookieRedirectException should return Http with status 400`() { - val exception = CookieRedirectException(hostname = "example.com") + val exception = CookieRedirectException(hostname = "example.com", message = "Fail!") val result = exception.toBitwardenError() @@ -24,13 +24,15 @@ class BitwardenErrorTest { @Test fun `toBitwardenError with CookieRedirectException should include message in body`() { - val exception = CookieRedirectException(hostname = "example.com") + val message = "Your request was interrupted because " + + "the app needed to re-authenticate. Please try again." + val exception = CookieRedirectException(hostname = "example.com", message = message) val result = exception.toBitwardenError() val httpError = result as BitwardenError.Http val body = httpError.responseBodyString - assertTrue(body?.contains(exception.message.orEmpty()) == true) + assertTrue(body?.contains(message) == true) } @Test diff --git a/network/src/test/kotlin/com/bitwarden/network/util/ExceptionExtensionsTest.kt b/network/src/test/kotlin/com/bitwarden/network/util/ExceptionExtensionsTest.kt index f46af708fc..8724e96f1b 100644 --- a/network/src/test/kotlin/com/bitwarden/network/util/ExceptionExtensionsTest.kt +++ b/network/src/test/kotlin/com/bitwarden/network/util/ExceptionExtensionsTest.kt @@ -24,7 +24,7 @@ class ExceptionExtensionsTest { fun `parseErrorBodyOrNull with CookieRedirectException should extract message`() { val expectedMessage = "Your request was interrupted because the app " + "needed to re-authenticate. Please try again." - val error = CookieRedirectException(hostname = "example.com") + val error = CookieRedirectException(hostname = "example.com", message = expectedMessage) .toBitwardenError() val result = error.parseErrorBodyOrNull( diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 3eda81fe66..5eecc5f571 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -1359,4 +1359,5 @@ Do you want to switch to this account? License details Licenses There are no licenses in your vault. + Your request was interrupted because the app needed to re-authenticate. Please try again.