Skip to content

Instantly share code, notes, and snippets.

@zakrodionov
Forked from farhanjk/SampleAuthenticator.kt
Created February 27, 2020 07:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zakrodionov/40c25a6f2713a93c94c682956d75352d to your computer and use it in GitHub Desktop.
Save zakrodionov/40c25a6f2713a93c94c682956d75352d to your computer and use it in GitHub Desktop.
Sample Okhttp3 Authenticator
/*
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