mirror of
https://github.com/bitwarden/android.git
synced 2026-03-09 11:44:41 -05:00
PM-24481: Logout when token refresh API returns 401 or 403 (#5651)
This commit is contained in:
@@ -40,4 +40,18 @@ sealed class RefreshTokenResponseJson {
|
||||
) : RefreshTokenResponseJson() {
|
||||
val isInvalidGrant: Boolean get() = error == "invalid_grant"
|
||||
}
|
||||
|
||||
/**
|
||||
* Models a failure response with a 403 "Forbidden" response code.
|
||||
*/
|
||||
data class Forbidden(
|
||||
val error: Throwable,
|
||||
) : RefreshTokenResponseJson()
|
||||
|
||||
/**
|
||||
* Models a failure response with a 401 "Unauthorized" response code.
|
||||
*/
|
||||
data class Unauthorized(
|
||||
val error: Throwable,
|
||||
) : RefreshTokenResponseJson()
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.bitwarden.network.util.DeviceModelProvider
|
||||
import com.bitwarden.network.util.NetworkErrorCode
|
||||
import com.bitwarden.network.util.base64UrlEncode
|
||||
import com.bitwarden.network.util.executeForNetworkResult
|
||||
import com.bitwarden.network.util.getNetworkErrorCodeOrNull
|
||||
import com.bitwarden.network.util.parseErrorBodyOrNull
|
||||
import com.bitwarden.network.util.toResult
|
||||
import kotlinx.serialization.json.Json
|
||||
@@ -131,13 +132,28 @@ internal class IdentityServiceImpl(
|
||||
.executeForNetworkResult()
|
||||
.toResult()
|
||||
.recoverCatching { throwable ->
|
||||
throwable
|
||||
.toBitwardenError()
|
||||
val bitwardenError = throwable.toBitwardenError()
|
||||
bitwardenError
|
||||
.parseErrorBodyOrNull<RefreshTokenResponseJson.Error>(
|
||||
code = NetworkErrorCode.BAD_REQUEST,
|
||||
json = json,
|
||||
)
|
||||
?: throw throwable
|
||||
?: run {
|
||||
when (bitwardenError.getNetworkErrorCodeOrNull()) {
|
||||
NetworkErrorCode.UNAUTHORIZED -> {
|
||||
RefreshTokenResponseJson.Unauthorized(throwable)
|
||||
}
|
||||
|
||||
NetworkErrorCode.FORBIDDEN -> {
|
||||
RefreshTokenResponseJson.Forbidden(throwable)
|
||||
}
|
||||
|
||||
NetworkErrorCode.BAD_REQUEST,
|
||||
NetworkErrorCode.TOO_MANY_REQUESTS,
|
||||
null,
|
||||
-> throw throwable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun registerFinish(
|
||||
|
||||
@@ -5,6 +5,14 @@ import com.bitwarden.network.model.BitwardenError
|
||||
import kotlinx.serialization.json.Json
|
||||
import retrofit2.HttpException
|
||||
|
||||
/**
|
||||
* Returns the [NetworkErrorCode] for the given error if it is available.
|
||||
*/
|
||||
internal fun BitwardenError.getNetworkErrorCodeOrNull(): NetworkErrorCode? =
|
||||
(this as? BitwardenError.Http)?.let { httpError ->
|
||||
NetworkErrorCode.entries.firstOrNull { httpError.code == it.code }
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse the error body to serializable type [T].
|
||||
*
|
||||
|
||||
@@ -7,5 +7,7 @@ internal enum class NetworkErrorCode(
|
||||
val code: Int,
|
||||
) {
|
||||
BAD_REQUEST(code = 400),
|
||||
UNAUTHORIZED(code = 401),
|
||||
FORBIDDEN(code = 403),
|
||||
TOO_MANY_REQUESTS(code = 429),
|
||||
}
|
||||
|
||||
@@ -333,6 +333,20 @@ class IdentityServiceTest : BaseServiceTest() {
|
||||
assertTrue(result.isFailure)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `refreshTokenSynchronously when response is a 403 error should return an Forbidden`() {
|
||||
server.enqueue(MockResponse().setResponseCode(403))
|
||||
val result = identityService.refreshTokenSynchronously(refreshToken = REFRESH_TOKEN)
|
||||
assertTrue(result.getOrThrow() is RefreshTokenResponseJson.Forbidden)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `refreshTokenSynchronously when response is a 401 error should return an Unauthorized`() {
|
||||
server.enqueue(MockResponse().setResponseCode(401))
|
||||
val result = identityService.refreshTokenSynchronously(refreshToken = REFRESH_TOKEN)
|
||||
assertTrue(result.getOrThrow() is RefreshTokenResponseJson.Unauthorized)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `registerFinish success json should be Success`() = runTest {
|
||||
val expectedResponse = RegisterResponseJson.Success(
|
||||
|
||||
Reference in New Issue
Block a user