Don't throw MALTokenExpired whenever we fail to refresh MAL token

Also cleanup
This commit is contained in:
AntsyLich 2024-01-30 02:22:29 +06:00
parent ddbe8efbc5
commit 0f4de03d7a
No known key found for this signature in database
2 changed files with 34 additions and 38 deletions

View file

@ -21,9 +21,8 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor
} }
val originalRequest = chain.request() val originalRequest = chain.request()
// Refresh access token if expired if (oauth?.isExpired() == true) {
if (oauth != null && oauth!!.isExpired()) { refreshToken(chain)
setAuth(refreshToken(chain))
} }
if (oauth == null) { if (oauth == null) {
@ -36,27 +35,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.build() .build()
val response = chain.proceed(authRequest) return chain.proceed(authRequest)
val tokenIsExpired = response.headers["www-authenticate"]
?.contains("The access token expired") ?: false
// Retry the request once with a new token in case it was not already refreshed
// by the is expired check before.
if (response.code == 401 && tokenIsExpired) {
response.close()
val newToken = refreshToken(chain)
setAuth(newToken)
val newRequest = originalRequest.newBuilder()
.addHeader("Authorization", "Bearer ${newToken.access_token}")
.header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})")
.build()
return chain.proceed(newRequest)
}
return response
} }
/** /**
@ -68,22 +47,37 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor
myanimelist.saveOAuth(oauth) myanimelist.saveOAuth(oauth)
} }
private fun refreshToken(chain: Interceptor.Chain): OAuth { private fun refreshToken(chain: Interceptor.Chain): OAuth = synchronized(this) {
if (tokenExpired) throw MALTokenExpired()
oauth?.takeUnless { it.isExpired() }?.let { return@synchronized it }
val response = try {
chain.proceed(MyAnimeListApi.refreshTokenRequest(oauth!!))
} catch (_: Throwable) {
throw MALTokenRefreshFailed()
}
if (response.code == 401) {
myanimelist.setAuthExpired()
throw MALTokenExpired()
}
return runCatching { return runCatching {
val oauthResponse = chain.proceed(MyAnimeListApi.refreshTokenRequest(oauth!!)) if (response.isSuccessful) {
if (oauthResponse.code == 401) { with(json) { response.parseAs<OAuth>() }
myanimelist.setAuthExpired()
}
if (oauthResponse.isSuccessful) {
with(json) { oauthResponse.parseAs<OAuth>() }
} else { } else {
oauthResponse.close() response.close()
null null
} }
} }
.getOrNull() .getOrNull()
?: throw MALTokenExpired() ?.also {
this.oauth = it
myanimelist.saveOAuth(it)
}
?: throw MALTokenRefreshFailed()
} }
} }
class MALTokenRefreshFailed : IOException("MAL: Failed to refresh account token")
class MALTokenExpired : IOException("MAL: Login has expired") class MALTokenExpired : IOException("MAL: Login has expired")

View file

@ -5,14 +5,16 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class OAuth( data class OAuth(
val token_type: String,
val refresh_token: String, val refresh_token: String,
val access_token: String, val access_token: String,
val token_type: String,
val created_at: Long = System.currentTimeMillis(),
val expires_in: Long, val expires_in: Long,
) val created_at: Long = System.currentTimeMillis(),
) {
fun OAuth.isExpired() = System.currentTimeMillis() > created_at + (expires_in * 1000) // Assumes expired a minute earlier
private val adjustedExpiresIn: Long = (expires_in - 60) * 1000
fun isExpired() = created_at + adjustedExpiresIn < System.currentTimeMillis()
}
fun Track.toMyAnimeListStatus() = when (status) { fun Track.toMyAnimeListStatus() = when (status) {
MyAnimeList.READING -> "reading" MyAnimeList.READING -> "reading"