Automating Auth Token Injection in Retrofit with OkHttp Interceptors

hyuwah

Muhamad Wahyudin

Posted on November 21, 2024

Automating Auth Token Injection in Retrofit with OkHttp Interceptors

When working with authenticated APIs in Android development, injecting an Authorization token into HTTP headers can be repetitive and error-prone. A clean and automated approach is to use OkHttp Interceptors combined with a custom annotation in Retrofit. This not only simplifies the process but also keeps your codebase clean and maintainable.

In this article, we’ll explore how to achieve this by leveraging an interceptor and annotations to handle token injection dynamically.


The Idea

The goal is to:

  1. Use a custom annotation, e.g., @InjectAuth, on Retrofit API methods that require an Authorization header.
  2. Automatically inject the auth token as Authorization header into the request headers for annotated methods.
  3. Keep the rest of the API calls unaffected.

Here’s how we can accomplish this.

Sequence of Events

Here’s the high-level flow when making an API request:

  • Client invokes a Retrofit method annotated with @InjectAuth.
  • Retrofit delegates the call to the OkHttp Interceptor.
  • The interceptor checks for the annotation and appends the token to the header if required.
  • The server processes the authenticated request and returns a response.

This interaction is illustrated in the following sequence diagram:

sequence of events

Implementing the Auth Token Interceptor

The core of our implementation is the custom AuthInterceptor. This interceptor checks for the @InjectAuth annotation and dynamically appends the auth token.

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class InjectAuth
Enter fullscreen mode Exit fullscreen mode

The interceptor implementation:

class AuthInterceptor(private val tokenProvider: () -> String?) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()

        if (request.markedForInjection()) {
            val token = tokenProvider.invoke()
            if (!token.isNullOrEmpty()) {
                val newRequest = request.newBuilder()
                    .addHeader("Authorization", "Bearer $token")
                    .build()
                return chain.proceed(newRequest)
            }
        }

        return chain.proceed(request)
    }

     /**
     * Check if request is annotated with `@InjectAuth` annotation,
     * If annotated, then it's marked for `Authorization` injection
     */
    private fun Request.markedForInjection(): Boolean = tag<Invocation>()?.method()
        ?.annotations?.toSet()?.find { it is InjectAuth } != null
}
Enter fullscreen mode Exit fullscreen mode

Setting Up Retrofit

You need to add the AuthInterceptor to OkHttp’s interceptor chain when building the Retrofit instance:

val tokenProvider: () -> String? = { 
    /* Logic to fetch token (i.e. get from shared preferences) */ 
}

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(AuthInterceptor(tokenProvider))
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .build()

val apiService = retrofit.create<ApiService>()
Enter fullscreen mode Exit fullscreen mode

Annotating API Methods

Use the @InjectAuth annotation for any Retrofit API method that requires authentication.

interface ApiService {
    @InjectAuth
    @GET("protected/endpoint")
    suspend fun getProtectedData(): Response<ProtectedData>

    @GET("public/endpoint")
    suspend fun getPublicData(): Response<PublicData>
}
Enter fullscreen mode Exit fullscreen mode

In this example:

  • getProtectedData will include the Authorization header automatically.
  • getPublicData will remain unaffected.

Key Benefits

  • Code Reusability: No need to manually add headers to every request.
  • Scalability: New endpoints requiring authentication can easily adopt this by using @InjectAuth annotation.
  • Centralized Token Management: The interceptor ensures token handling is managed in one place.

Conclusion

By combining Retrofit, OkHttp, and custom annotations, you can build a robust and automated mechanism for handling API authentication. This approach improves code readability, scalability, and maintainability.

Have thoughts or questions? Share them in the comments below!

💖 💪 🙅 🚩
hyuwah
Muhamad Wahyudin

Posted on November 21, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related