Last active
October 30, 2023 15:14
-
-
Save naturalwarren/bc7f64f003a0e6034c6e74a2aa8917f6 to your computer and use it in GitHub Desktop.
An OkHttp Authenticator that performs token refreshes.
This file contains 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
/** | |
* Authenticator that attempts to refresh the client's access token. | |
* In the event that a refresh fails and a new token can't be issued an error | |
* is delivered to the caller. This authenticator blocks all requests while a token | |
* refresh is being performed. In-flight requests that fail with a 401 are | |
* automatically retried. | |
*/ | |
class AccessTokenAuthenticator( | |
private val tokenProvider: AccessTokenProvider | |
) : Authenticator { | |
override fun authenticate(route: Route?, response: Response): Request? { | |
// We need to have a token in order to refresh it. | |
val token = tokenProvider.token() ?: return null | |
synchronized(this) { | |
val newToken = tokenProvider.token() | |
// Check if the request made was previously made as an authenticated request. | |
if (response.request().header("Authorization") != null) { | |
// If the token has changed since the request was made, use the new token. | |
if (newToken != token) { | |
return response.request() | |
.newBuilder() | |
.removeHeader("Authorization") | |
.addHeader("Authorization", "Bearer $newToken") | |
.build() | |
} | |
val updatedToken = tokenProvider.refreshToken() ?: return null | |
// Retry the request with the new token. | |
return response.request() | |
.newBuilder() | |
.removeHeader("Authorization") | |
.addHeader("Authorization", "Bearer $updatedToken") | |
.build() | |
} | |
} | |
return null | |
} | |
} |
I find that in the time it takes the new token API call to complete, the response would have proceeded with a null token and failed.
How is this working for anyone? My usecase is that in event of a 401 we make an api call to get the new token (using the refreshToken) then
EDIT: I've found a solution. I basically stall till the refresh token call completes & is set:
private fun retryNewToken(oldToken: String?): String? {
while (oldToken == sessionManager.getAuthToken()) {
// Keep looping till call completes
}
if (sessionManager.getAuthToken().isNullOrEmpty()) return null
return sessionManager.getAuthToken()
}
For this approach to work, its imperative that failed token refreshes sets the token to null
in sessionManager
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@cyph3rcod3r That's what the synchronized is for I think.