diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/SubscriptionsManager.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/SubscriptionsManager.kt index 75d8520224de..2e84d3fa5234 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/SubscriptionsManager.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/SubscriptionsManager.kt @@ -206,8 +206,10 @@ interface SubscriptionsManager { /** * Signs the user out and deletes all the data from the device + * + * @param invalidateTokens if true, a request will be made to BE to invalidate refresh+access token pair. */ - suspend fun signOut() + suspend fun signOut(invalidateTokens: Boolean = true) /** * Returns a [String] with the URL of the portal or null otherwise @@ -372,9 +374,11 @@ class RealSubscriptionsManager @Inject constructor( } } - override suspend fun signOut() { - authRepository.getAccessTokenV2()?.run { - coroutineScope.launch { authClient.tryLogout(accessTokenV2 = jwt) } + override suspend fun signOut(invalidateTokens: Boolean) { + if (invalidateTokens) { + authRepository.getAccessTokenV2()?.run { + coroutineScope.launch { authClient.tryLogout(accessTokenV2 = jwt) } + } } authRepository.setAccessTokenV2(null) authRepository.setRefreshTokenV2(null) @@ -587,7 +591,7 @@ class RealSubscriptionsManager @Inject constructor( Refresh token appears to be valid, but the related account doesn't exist in BE. After the subscription expires, BE eventually deletes the account, so this is expected. */ - signOut() + signOut(invalidateTokens = false) throw e } @@ -605,7 +609,7 @@ class RealSubscriptionsManager @Inject constructor( StoreLoginResult.Failure.AuthenticationError, -> { pixelSender.reportAuthV2InvalidRefreshTokenSignedOut() - signOut() + signOut(invalidateTokens = false) throw e } @@ -817,8 +821,10 @@ class RealSubscriptionsManager @Inject constructor( refreshSubscriptionData() } catch (e: HttpException) { when (e.code()) { - 400, 404 -> {} // expected if this is a first ever purchase using this account - ignore - 401 -> signOut() // access token was rejected even though it's not expired - can happen if the account was removed from BE + // expected if this is a first ever purchase using this account - ignore + 400, 404 -> {} + // access token was rejected even though it's not expired - can happen if the account was removed from BE + 401 -> signOut(invalidateTokens = false) else -> throw e } } diff --git a/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/RealSubscriptionsManagerTest.kt b/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/RealSubscriptionsManagerTest.kt index 6cd255d76363..aa943f0a602f 100644 --- a/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/RealSubscriptionsManagerTest.kt +++ b/subscriptions/subscriptions-impl/src/test/java/com/duckduckgo/subscriptions/impl/RealSubscriptionsManagerTest.kt @@ -65,6 +65,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.ResponseBody.Companion.toResponseBody @@ -820,6 +821,10 @@ class RealSubscriptionsManagerTest(private val authApiV2Enabled: Boolean) { assertNull(authRepository.getSubscription()) verify(pixelSender).reportAuthV2InvalidRefreshTokenDetected() verify(pixelSender).reportAuthV2InvalidRefreshTokenSignedOut() + + // Access token is invalid, so call to /logout can't succeed anyway. + advanceUntilIdle() + verify(authClient, never()).tryLogout(any()) } @Test @@ -844,6 +849,10 @@ class RealSubscriptionsManagerTest(private val authApiV2Enabled: Boolean) { assertNull(authRepository.getAccount()) assertNull(authRepository.getSubscription()) + // Account doesn't exist, so there is no point in notifying BE about logout. + advanceUntilIdle() + verify(authClient, never()).tryLogout(any()) + // Store login has 0 chance of success when account doesn't exist, so there should be no attempt. verify(authClient, never()).authorize(any()) verify(authClient, never()).storeLogin(any(), any(), any())