Android Intercepting API Header, Request, and Response

Chetan Gaikwad
3 min readOct 25, 2022

--

Table of content:

  1. Intercepting API header
  2. Intercepting API request
  3. Determining the Type of API call GET, POST, PATCH
  4. Intercepting API response

Prerequisite of the blog:
Basic understanding of android application development, Okhttp Library.

If you have ever built an android application that requires you to perform network calls you would have already come across and used the OkHttp library. You can read about OkHttp in detail here:
https://github.com/square/okhttp

What is API interception?
Interception is the means of preventing something from reaching its destination. When you wish to intercept an API call/network call from reaching its destination we use API interception. Interception can be both ways incoming and outgoing.

When would I need to intercept an API?

  1. The most common use case is you would like to add an authorization header to all the outgoing API requests
  2. You may wish to change the request body or the response body of your API calls. A common use case for this would be when you wish to perform encryption and decryption for your network calls

How to intercept an API request or response?

Create a class say MyHttpInterceptor that implements okhttp3.Interceptor interface and override intercept() method. While creating a OkHttpClient() object specifies the object of HttpInterceptor class using addInterceptor() method.

Intercepting API Header:

class MyHttpInterceptor : Interceptor {

override fun intercept(chain: Interceptor.Chain): Response {
val original: Request = chain.request()

val request: Request = original.newBuilder()
.header("x-access-token", "JWT Token")
.build()

val response = chain.proceed(request)

return chain.proceed(request)
}
}
val okHttpClient = OkHttpClient()
.newBuilder()
.addInterceptor(myHttpInterceptor)
.addNetworkInterceptor(interceptor)
.build()

Determining the Type of API call GET, POST, PATCH:

We can also determine the type(GET, POST, PATCH) of the API request while intercepting the API request. If we have to intercept say only POST requests then this is useful.

In the above code snippet, original.method() will give the method type.

Intercepting & Updating API request body:

In some secured applications, it might be a need for the backend to send the data in an encrypted format. For such use cases, we can intercept an API request body, update the body and send the updated body to the API request. The following code demonstrates this flow

class MyHttpInterceptor : Interceptor {

override fun intercept(chain: Interceptor.Chain): Response {
val original: Request = chain.request()

val request: Request = original.newBuilder()
.header("x-access-token", "JWT Token")
.method(
original.method,
if (original.method == "POST")
getFormBody(original)
else original.body
).build()

return chain.proceed(request)
}

private fun getFormBody(original: Request): FormBody {
val original = bodyToString(original.body)

//Update the body here

return FormBody.Builder()
.add("payload", updatedbody)
.build()
}

fun bodyToString(request: RequestBody?): String? {
return try {
val buffer = Buffer()
if (request != null)
request.writeTo(buffer)
else return ""
buffer.readUtf8()
} catch (e: IOException) {
"did not work"
}
}
}

Intercepting & Updating API response body:

we will have to Re-create the response before returning it because the body can be read only once.

class MyHttpInterceptor : Interceptor {

override fun intercept(chain: Interceptor.Chain): Response {
val original: Request = chain.request()

val request: Request = original.newBuilder()
.header("x-access-token", "JWT Token")
.method(
original.method,
if (original.method == "POST")
getFormBody(original)
else original.body
).build()

val response = chain.proceed(request)

val rawJson: String = response.body?.string()!!
var decryptedResponse = ""
if (response.isSuccessful) {
try {
val objectValue =
JSONTokener(rawJson).nextValue()
decryptedResponse = decrypt(AES_KEY, objectValue as String)
Log.d("jsonLog", decryptedResponse)
} catch (e: JSONException) {
e.printStackTrace()
}
}

// Re-create the response before returning it because body can be read only once
return response.newBuilder()
.body(
(
if (response.isSuccessful)
decryptedResponse
else rawJson
).toResponseBody(response.body?.contentType())
).build()
}

private fun getFormBody(original: Request): FormBody {
val original = bodyToString(original.body)

//Update the body here

return FormBody.Builder()
.add("payload", updatedbody)
.build()
}

fun bodyToString(request: RequestBody?): String? {
return try {
val buffer = Buffer()
if (request != null)
request.writeTo(buffer)
else return ""
buffer.readUtf8()
} catch (e: IOException) {
"did not work"
}
}
}

If you have to Monitor & ModifyAndroid app network traffic via MITM proxy then please refer

https://gaikwadchetan93.medium.com/monitoring-modifying-android-app-network-traffic-via-mitm-proxy-part-1-886f6324f705

Let’s get connected

For any query, suggestion, or improvement on my blog ping me

--

--