Interview QuestionPractical QuestionFollow-up Questions

Refreshing OAuth Tokens with OkHttp Authenticator and Interceptors

skydovesJaewoong Eum (skydoves)||7 min read

Refreshing OAuth Tokens with OkHttp Authenticator and Interceptors

When working with APIs secured by OAuth, handling token expiration and refresh is a critical part of the networking layer. OkHttp provides two mechanisms for this: the Authenticator interface and Interceptor. The Authenticator is specifically designed for responding to 401 authentication challenges, while an Interceptor provides more flexible control over both request and response processing. Understanding when to use each approach and how they interact with the request lifecycle is essential for building a reliable authentication layer. By the end of this lesson, you will be able to:

  • Explain how the OkHttp Authenticator responds to 401 challenges automatically.
  • Implement an Interceptor that detects token expiration and refreshes the token inline.
  • Describe the key differences between Authenticator and Interceptor for token management.
  • Handle thread safety concerns when multiple requests trigger a token refresh simultaneously.

Using OkHttp Authenticator

The Authenticator interface is invoked automatically when a server responds with a 401 Unauthorized status code. It provides a hook to supply updated credentials and retry the request transparently.

class TokenAuthenticator(
    private val tokenProvider: TokenProvider
) : Authenticator {
    override fun authenticate(route: Route?, response: Response): Request? {
        val newToken = tokenProvider.refreshToken() ?: return null
        return response.request.newBuilder()
            .header("Authorization", "Bearer $newToken")
            .build()
    }
}

Returning null from authenticate signals that the request should not be retried. Returning a new Request causes OkHttp to retry with the updated headers. The client is configured by passing the authenticator to the builder:

val okHttpClient = OkHttpClient.Builder()
    .authenticator(TokenAuthenticator(tokenProvider))
    .build()

OkHttp limits the number of retry attempts to prevent infinite loops when the server keeps returning 401.

Using OkHttp Interceptor

An Interceptor offers more control over the entire request and response pipeline. It can attach the token to every outgoing request and handle refresh logic when a 401 is detected.

class TokenInterceptor(
    private val tokenProvider: TokenProvider
) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request().newBuilder()
            .header("Authorization", "Bearer ${tokenProvider.getToken()}")
            .build()
        val response = chain.proceed(request)
        if (response.code == 401) {
            synchronized(this) {
                val newToken = tokenProvider.refreshToken() ?: return response
                request = request.newBuilder()
                    .header("Authorization", "Bearer $newToken").build()
                response.close()
                return chain.proceed(request)
            }
        }
        return response
    }
}

This interview continues for subscribers

Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.

Become a Sponsor