Making an Authorized Request to the E*TRADE API with OkHttp

Picture of author

By Sean Soper

October 20, 2020

Photo by Rowan Heuvel on Unsplash.

Introduction

While the default Java HttpClient is just fine for simple requests, we will be interacting with an OAuth v1 powered endpoint which will require requests to be signed. We shouldn’t have to worry about all those details and for that the OkHttp library fits our needs perfectly.

This post builds on work previously done in Reading a YAML Configuration File in Kotlin.

OkHttp

First we are going to bring in OkHttp which we will use as a base on which to build our HTTP client.

# build.gradle.kts

dependencies {
  …
  implementation("com.squareup.okhttp3:okhttp:4.9.0")
}

OkHttp implements the Builder pattern which is straight out of GoF’s Design Patterns. We can verify the new dependency works with a simple example.

val client = OkHttpClient.Builder().build()

val request = Request.Builder()
        .url("https://www.google.com")
        .build()

val response = client.newCall(request).execute()

response.body?.let {
    println(it.string())
}

Intercept

One of the many great things about the OkHttp library is its extensibility particularly the ease with which we can add an interceptor. In our case, we will need an Oauth v1 interceptor which can sign our requests with the E*TRADE supplied keys we received. Fortunately, most of the hard work has been done for us. This interceptor, which I updated to work with E*TRADE endpoints, was forked off the Kotlin version of an OAuth v1 interceptor originally written by Jake Wharton.

But rather than just dump all our code in the root of our project, we are going to create a package. With an eye towards supporting more than one broker in the future it makes sense to organize and componentize it for ease of maintenance.

Package Screenshot

We are going to name our package connectors and when you add a new file/class within this package you will notice that the package identifier at the top of the file will be automatically set to the correct value. Any files in your project that make use of a class in the package will need to reference the full package name. Let’s go ahead and add Etrade.kt and EtradeInterceptor.kt to our new package.

With the Interceptor doing the heavy lifting of signing our requests, we can focus on crafting the correct URL request to get our OAuth tokens. While additional work would be required to parse the URL-encoded response, here is what the base functionality would look like.

val keys = OauthKeys(
    consumerKey = consumerKey,
    consumerSecret = consumerSecret
)

val client = OkHttpClient.Builder()
    .addInterceptor(EtradeInterceptor(keys))
    .build()

val request = Request.Builder()
    .url("https://apisb.etrade.com/oauth/request_token")
    .build()

val response = client.newCall(request).execute()

Tokens

With our consumer keys stored in our YAML file for easy retrieval, we can pass them along to our HTTP client and get back the OAuth tokens required for the next step in our journey.

val client = Etrade(configuration, parsed.production)

client.requestToken().apply {
    println("Token: $accessToken, secret: $accessSecret")
}

A summary of these commits can be found here.