Goodreads OAuth in Android

For my solo project, I’m building a Goodraeds client. Mostly because I like to use Goodreads to track the books I’m reading and I don’t enjoy the default Android app provided by Goodreads.

Thus, I’m building my own alternative using their API. However, it has been a tricky process from the beginning since

  1. Goodreads API isn’t very well documented.
  2. Logging in via Goodreads means that I need to do OAuth authentication with their API, which as mentioned in point 1, is not very well documented, and also is OAuth 1.0.

It took me some time to figure out how to perform OAuth authentication in Android development and I’ll go over the steps on how to do this in the hopes that it’ll save other people a bunch of time.

Just as a note, since this is Android development, the code samples that I’ll be showing will work in any standard Java application or any language that can easily interpolate with Java and targets the JVM. Also, since I’m writing this app in Kotlin, do note that the code samples will be in Kotlin with the exception of one small portion that relies on a library. But if you need to use Java, you can always look at the code sample and figure out what’s going on pretty easily.

Things that we will need

We’ll need several components that come together to make OAuth authentication work. They are:

  • Goodreads developer key and secrets
  • Your own server that will act as a callback url for the Goodreads OAuth API
  • ScribeJava library for making OAuth easy

As far as setting up your own server that will act as a callback url for the Goodreads OAuth API, you’re on your own. What you’ll have to do is set up a simple API that will receive the callback url that includes the OAuth token and whether the user authenticated your app from accessing his/her data or not from Goodreads API with a route that you’ll set in the Goodreads API Developer dashboard.

1 – Register for the Goodreads developer keys

Just Google something like Goodreads API developer keys and you’ll find a link in Goodreads that will allow you to register for the developer keys so that you can access their APIs. Once you have registered yourself as a developer, you’ll be redirected to a page that looks like this that will provide you with your developer key, secret, and form with inputs where you can enter in your app’s name, domain, and a callback url. Fill these out appropriately with the exception of the callback url.

2 – Set up your own API to act as a callback url

I won’t go into the details of how to set up your own API to act as a callback url. It shouldn’t be too difficult if you’re somewhat familiar with backend development. All this server has to do is take in two parameters, oauth_token and authorize which will be coming from the Goodreads API to your callback server, and then render the results in a json format. I like to user Ruby on Rails for backend development purposes and for the sake of providing a simple example, this is what my controller looks like.

And my routes file looks like

Having my Rails controllers and routes like this will give me a url that goes something like https://myurl.com/goodreads_oauth_callback.

Once you have deployed your server, go back to the Goodreads page where your developer keys are listed, and fill out the Callback URL form. Just as an example, mine looks like this (sensitive data blurred out).

3 – Install ScribeJava

Depending on what you’re developing, you can do this several ways. In the context of Android development specifically, simply add this line in your app’s build.gradle file.

Notice here that I’m using an older version of ScribeJava. As of this writing, the latest version of ScribeJava is at 5.3.0. For whatever reason, I couldn’t get Goodreads OAuth to work with the latest version of ScribeJava as I kept running into an exception that I couldn’t find a solution to. Thus, rather than trying to figure out how to make the latest version of ScribeJava work, I decided to simply use a version that worked out of the box. If you want to try to get the latest version of ScribeJava to work, give it a try. If you want the easy way and get Goodreads OAuth authentication to work following my code samples, stick with version 4.2.0.

4 – Set up a Goodreads API adapter for ScribeJava

ScribeJava comes with a lot of different adapters to help make it easier for the user to work with various popular OAuth authentication services like Twitter, Facebook, and etc. However, it does not come with an adapter for Goodreads. Thus, we’ll have to write one. Don’t worry, it’s fairly simple. We’ll write this one in Java instead of Kotlin. Also, since Kotlin interpolates with Java, feel free to write this one in Kotlin if you desire.

5 – Authenticate the user via OAuth

This is the part where we’ll put everything together so that users can use Goodreads OAuth to authenticate with your app. One thing about the Goodreads OAuth is that user authentication happens via their login page. Thus, whatever you’re building, you’ll have to redirect your users to a webview of some sort to display the login form provided by Goodreads. The code sample below is for Android development, thus we’ll be using the standard Android WebView. If you’re not doing Android development, adjust your code accordingly.

While the above code should be relatively easy to follow and understand, I’ll explain the most important part where we retrieve the authenticated user’s access tokens and secrets that we’ll be using to make requests to the Goodreads API.

You’ll see that I override onPageFinished method in my GoodreadsAuthView webview. In this overridden method, I check to see if the url in the WebView contains the callback url that we defined in the Goodreads developer page. If it does, we will grab the query parameters in that url to retrieve the oauth token and whether the user has authorized your app to access his/her data. If the user has authorized your app to access his/her Goodreads data, the query parameter authorize will return a String value of 1.

From here, we check to see if the query parameter authorize has returned 1, and then retrieve the access token and the access token secret utilizing the ScribeJava library. We then use these tokens and the ScribeJava library to make special OAuth request to the https://www.goodreads.com/api/auth_user endpoint so that we can parse out the authenticated user’s user_id. The /api/auth_user endpoint is a regular endpoint defined in the Goodreads API documentation, but for whatever reason, I found it close to impossible to try to retrieve this with regular curl requests (or using Retrofit in Android). However, if I use ScribeJava’s built in functions, retrieving the user_id of the authenticated user is very simple. To be transparent, this part where we try to retrieve the user_id of the authenticated users is the part where most developers seemed to be having trouble based on the questions posted in the Goodreads developer forums. ScribeJava makes this process simple as one line of code.

Note that once we sign the request using the access token and ScribeJava’s built in functionality, we need to parse out the user_id from the XML that gets returned from the Goodreads API. Once we have retrieved the user_id, we can go ahead and save the user_id, access_token, access_token_secret, and then oauth_token in a SharedPreferences so that we can use them throughout the rest of the codebase.

Conclusion

This actually took me around 2 days to figure out how to do. Yes, it seems long to figure out how to do a simple OAuth authentication, but it’s a lot more difficult than it should be due to the lack of support and documentation from Goodreads. I’m hoping that this blog post helps anyone who’s trying to work with the Goodreads API. If you have any questions, feel free to leave a comment below or email me directly. Thanks for reading.