mirror of
https://github.com/bitwarden/android.git
synced 2026-08-28 22:45:25 -05:00
PM-24481: Logout when token refresh API returns 401 or 403 (#5651)
This commit is contained in:
@@ -781,12 +781,21 @@ class AuthRepositoryImpl(
|
||||
when (refreshTokenResponse) {
|
||||
is RefreshTokenResponseJson.Error -> {
|
||||
if (refreshTokenResponse.isInvalidGrant) {
|
||||
// We only logout for an invalid grant
|
||||
logout(userId = userId, reason = LogoutReason.InvalidGrant)
|
||||
}
|
||||
IllegalStateException(refreshTokenResponse.error).asFailure()
|
||||
}
|
||||
|
||||
is RefreshTokenResponseJson.Forbidden -> {
|
||||
logout(userId = userId, reason = LogoutReason.RefreshForbidden)
|
||||
refreshTokenResponse.error.asFailure()
|
||||
}
|
||||
|
||||
is RefreshTokenResponseJson.Unauthorized -> {
|
||||
logout(userId = userId, reason = LogoutReason.RefreshUnauthorized)
|
||||
refreshTokenResponse.error.asFailure()
|
||||
}
|
||||
|
||||
is RefreshTokenResponseJson.Success -> {
|
||||
// Store the new token information
|
||||
authDiskSource.storeAccountTokens(
|
||||
|
||||
@@ -35,6 +35,18 @@ sealed class LogoutReason {
|
||||
*/
|
||||
data object InvalidGrant : LogoutReason()
|
||||
|
||||
/**
|
||||
* Indicates that the logout is happening because the there was a "Forbidden" response from
|
||||
* token refresh API.
|
||||
*/
|
||||
data object RefreshForbidden : LogoutReason()
|
||||
|
||||
/**
|
||||
* Indicates that the logout is happening because the there was a "Unauthorized" response from
|
||||
* token refresh API.
|
||||
*/
|
||||
data object RefreshUnauthorized : LogoutReason()
|
||||
|
||||
/**
|
||||
* Indicates that the logout is happening because of an invalid state.
|
||||
*/
|
||||
|
||||
+79
-12
@@ -882,21 +882,88 @@ class AuthRepositoryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `refreshAccessTokenSynchronously returns failure and logs out on failure`() = runTest {
|
||||
fakeAuthDiskSource.storeAccountTokens(
|
||||
userId = USER_ID_1,
|
||||
accountTokens = ACCOUNT_TOKENS_1,
|
||||
)
|
||||
coEvery {
|
||||
identityService.refreshTokenSynchronously(REFRESH_TOKEN)
|
||||
} returns Throwable("Fail").asFailure()
|
||||
fun `refreshAccessTokenSynchronously returns failure if refreshTokenSynchronously fails`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.storeAccountTokens(
|
||||
userId = USER_ID_1,
|
||||
accountTokens = ACCOUNT_TOKENS_1,
|
||||
)
|
||||
coEvery {
|
||||
identityService.refreshTokenSynchronously(REFRESH_TOKEN)
|
||||
} returns Throwable("Fail").asFailure()
|
||||
|
||||
assertTrue(repository.refreshAccessTokenSynchronously(USER_ID_1).isFailure)
|
||||
assertTrue(repository.refreshAccessTokenSynchronously(USER_ID_1).isFailure)
|
||||
|
||||
coVerify(exactly = 1) {
|
||||
identityService.refreshTokenSynchronously(REFRESH_TOKEN)
|
||||
coVerify(exactly = 1) {
|
||||
identityService.refreshTokenSynchronously(REFRESH_TOKEN)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `refreshAccessTokenSynchronously returns logs out and returns failure if refreshTokenSynchronously returns invalid_grant`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
fakeAuthDiskSource.storeAccountTokens(
|
||||
userId = USER_ID_1,
|
||||
accountTokens = ACCOUNT_TOKENS_1,
|
||||
)
|
||||
coEvery {
|
||||
identityService.refreshTokenSynchronously(REFRESH_TOKEN)
|
||||
} returns RefreshTokenResponseJson.Error(error = "invalid_grant").asSuccess()
|
||||
|
||||
assertTrue(repository.refreshAccessTokenSynchronously(USER_ID_1).isFailure)
|
||||
|
||||
coVerify(exactly = 1) {
|
||||
identityService.refreshTokenSynchronously(REFRESH_TOKEN)
|
||||
userLogoutManager.logout(userId = USER_ID_1, reason = LogoutReason.InvalidGrant)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `refreshAccessTokenSynchronously returns logs out and returns failure if refreshTokenSynchronously returns Forbidden`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
fakeAuthDiskSource.storeAccountTokens(
|
||||
userId = USER_ID_1,
|
||||
accountTokens = ACCOUNT_TOKENS_1,
|
||||
)
|
||||
coEvery {
|
||||
identityService.refreshTokenSynchronously(REFRESH_TOKEN)
|
||||
} returns RefreshTokenResponseJson.Forbidden(error = Throwable("Fail!")).asSuccess()
|
||||
|
||||
assertTrue(repository.refreshAccessTokenSynchronously(USER_ID_1).isFailure)
|
||||
|
||||
coVerify(exactly = 1) {
|
||||
identityService.refreshTokenSynchronously(REFRESH_TOKEN)
|
||||
userLogoutManager.logout(userId = USER_ID_1, reason = LogoutReason.RefreshForbidden)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `refreshAccessTokenSynchronously returns logs out and returns failure if refreshTokenSynchronously returns Unauthorized`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
fakeAuthDiskSource.storeAccountTokens(
|
||||
userId = USER_ID_1,
|
||||
accountTokens = ACCOUNT_TOKENS_1,
|
||||
)
|
||||
coEvery {
|
||||
identityService.refreshTokenSynchronously(REFRESH_TOKEN)
|
||||
} returns RefreshTokenResponseJson.Unauthorized(error = Throwable("Fail!")).asSuccess()
|
||||
|
||||
assertTrue(repository.refreshAccessTokenSynchronously(USER_ID_1).isFailure)
|
||||
|
||||
coVerify(exactly = 1) {
|
||||
identityService.refreshTokenSynchronously(REFRESH_TOKEN)
|
||||
userLogoutManager.logout(
|
||||
userId = USER_ID_1,
|
||||
reason = LogoutReason.RefreshUnauthorized,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `refreshAccessTokenSynchronously returns success and sets account tokens`() = runTest {
|
||||
|
||||
Reference in New Issue
Block a user