Last active
May 19, 2022 20:20
-
-
Save farhanjk/ce9826ae3a5d5439eec6b8815ebd61d2 to your computer and use it in GitHub Desktop.
Sample Okhttp3 Authenticator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
SampleAuthenticator (c) by Farhan Khan | |
SampleAuthenticator is licensed under a | |
Creative Commons Attribution 3.0 Unported License. | |
http://creativecommons.org/licenses/by/3.0/ | |
*/ | |
class SampleAuthenticator( | |
lazyOtacRepository: Eval<OtacRepository>, | |
lazyPassCodeRepository: Eval<PassCodeRepository>, | |
lazyCurrentScreenHook: Eval<CurrentScreenHookProvider> | |
) : Authenticator { | |
private val otacRepository: OtacRepository by lazy { lazyOtacRepository.value() } | |
private val passCodeRepository: PassCodeRepository by lazy { lazyPassCodeRepository.value() } | |
private val currentScreenHook: CurrentScreenHook? by lazy { | |
lazyCurrentScreenHook.value().currentScreenHook | |
} | |
override fun authenticate(route: Route?, originalResponse: Response): Request? { | |
if (originalResponse.isUnauthorized()) { | |
if (!isEligibleForRefresh(originalResponse)) { | |
return null | |
} | |
if (isTokenRenewable() && retryCount(originalResponse) < 1) { | |
val authData = renewToken() | |
if (authData != null) { | |
if (canRetryRequest(originalResponse, authData)) { | |
return retryRequest( | |
originalResponse, | |
authData, | |
retryCount(originalResponse) + 1 | |
) | |
} | |
} else { | |
otacRepository.clearData() | |
currentScreenHook?.onUnAuthorizedError() | |
} | |
} else { | |
currentScreenHook?.onUnAuthorizedError() | |
} | |
} | |
return null | |
} | |
private fun Response.isUnauthorized() = this.code() == ErrorResultCode.HTTP_UNAUTHORIZED | |
private fun canRetryRequest(originalResponse: Response, currentAuthData: AuthData?) = | |
originalResponse.request().headers(Authorization.NAME).isNotEmpty() | |
&& originalResponse.request().headers(Authorization.NAME)[0] != currentAuthData?.accessToken | |
private fun isTokenRenewable() = | |
otacRepository.getData()?.canRefresh() ?: false | |
private fun isEligibleForRefresh(originalResponse: Response): Boolean { | |
return originalResponse.request().header("x-no-refresh") != true.toString() | |
} | |
private fun renewToken() = | |
runBlocking { | |
when (val result = | |
otacRepository.refresh(OtacRepository.RefreshInformation(passCodeRepository.getPassCode()))) { | |
is SafeResult.Success -> { | |
result.data | |
} | |
else -> { | |
null | |
} | |
} | |
} | |
private fun retryRequest( | |
originalResponse: Response, | |
currentAuthData: AuthData?, | |
retryCount: Int | |
) = | |
currentAuthData?.let { authData -> | |
originalResponse.request().newBuilder() | |
.removeHeader(Authorization.NAME) | |
.removeHeader(HEADER_RETRY_COUNT) | |
.addHeader(Authorization.NAME, Authorization.getValue(authData.accessToken)) | |
.addHeader(HEADER_RETRY_COUNT, "$retryCount") | |
.build() | |
} | |
private fun retryCount(response: Response?): Int { | |
return response?.request()?.header(HEADER_RETRY_COUNT)?.toInt() ?: 0 | |
} | |
companion object { | |
const val HEADER_RETRY_COUNT = "xInternalRetryCount" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment