How to Use Reddit OAuth 2.0 In Android Apps

  Programming

If you are building an Android client for Reddit, you cannot ask your users to type in their usernames and passwords directly into your app. Well, you could a few years ago, but not anymore. Reddit now expects all its mobile clients to follow the OAuth 2.0 protocol. Adding the OAuth flow to an Android app, however, is no trivial task. Last week, I spent about an hour learning how to implement it. By writing this tutorial, I hope to help you save some time.

Register your App

The first thing you need to do is register your app with Reddit. Therefore, go to the preferences > apps page of Reddit, and create a new app. If this is your first Reddit app, you’ll have to click on a button that says are you a developer? create an app….

In the form that pops up, give a name and description to your app and select installed app. In the about url field, you can type in the address of a page that is present on your website. For now, however, feel free to just say example.com.

In the redirect uri field, type in another URL, preferably one that belongs to your website. For now, you can simply say http://www.example.com/my_redirect. It’s okay if the page does not exist. It’s just the string that is important. Make a note of it.

app registration form

Finally, press the Create app button to get your client ID.

app registration form complete

Dependencies and Requirements

In this tutorial, we’ll be using the OkHttp library to communicate with Reddit’s servers. Therefore, add the following line to your Android Studio project’s build.gradle file:

compile 'com.squareup.okhttp3:okhttp:3.4.1'

Of course, we’ll also need the INTERNET permission. So, add the following line to your manifest:

<uses-permission android:name="android.permission.INTERNET" />

Create a Login Button

The user will be clicking on a button to initiate the login process. Therefore, your layout must have a button saying something like “Sign in to Reddit”.

<Button android:id="@+id/signin"
    android:text="Sign In With Reddit"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="startSignIn"/>

Note that the button I added has an onClick listener called startSignIn. So, inside the startSignIn() method of your Activity, you’ll be writing code to start the sign in process.

The users will be typing in their usernames and passwords on a sign in page that’s hosted on Reddit’s servers. Therefore, you must now create an Intent that opens a browser and takes the user to the sign in page.

But first, add a few final member variables to your Activity:

private static final String AUTH_URL =
    "https://www.reddit.com/api/v1/authorize.compact?client_id=%s" +
    "&response_type=code&state=%s&redirect_uri=%s&" +
    "duration=permanent&scope=identity";

private static final String CLIENT_ID = "ABCDEFGHIJKLM012345-AA";

private static final String REDIRECT_URI =
        "http://www.example.com/my_redirect";

private static final String STATE = "MY_RANDOM_STRING_1";

private static final String ACCESS_TOKEN_URL =
        "https://www.reddit.com/api/v1/access_token";

The names of the variables must be fairly intuitive. If you are wondering what the STATE variable is, it’s just a random string that you pass to the AUTH_URL. The same string will be present in the sign in result, and you can use it to make sure that the sign in result belongs to you.

Now, you can create a startSignIn() method and add the following code to it, which simply opens the AUTH_URL:

public void startSignIn(View view) {
    String url = String.format(AUTH_URL, CLIENT_ID, STATE, REDIRECT_URI);
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
    startActivity(intent);
}

Handling the Sign In Result

Once the user completes the sign in, Reddit’s server will redirect the browser to the redirect URI you mentioned. Therefore, to be able to get the sign in result, your app must handle that redirect URI. You can do so by adding an intent-filter for it inside you manifest file.

So, inside the declaration of your Activity, add the following code:

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <data android:host="www.example.com"
        android:scheme="http"
        android:path="/my_redirect" />
</intent-filter>

You must, of course, make sure that the host, scheme, and path attributes of the <data> tag match your redirect URI.

Once you add the intent-filter, your Activity will be automatically opened when the browser tries to visit the redirect URI. Your Activity will also receive an Intent object containing details about the sign in result.

You can receive the Intent in the onResume() method of your Activity. The Intent object’s data will be a Uri object whose query string contains a special code you can use to request for an access token. Before you try to extract code, however, you must check for errors, and also make sure that the state parameter matches the random string you defined earlier.

Accordingly, add the following code to your Activity:

@Override
protected void onResume() {
    super.onResume();

    if(getIntent()!=null && getIntent().getAction().equals(Intent.ACTION_VIEW)) {
        Uri uri = getIntent().getData();
        if(uri.getQueryParameter("error") != null) {
            String error = uri.getQueryParameter("error");
            Log.e(TAG, "An error has occurred : " + error);
        } else {
            String state = uri.getQueryParameter("state");
            if(state.equals(STATE)) {
                String code = uri.getQueryParameter("code");
                getAccessToken(code);
            }
        }
    }
}

Note that we are passing the code as an argument to a method called getAccessToken(). Let’s create that.

private void getAccessToken(String code) {

}

Inside the method, you must create a new instance of OkHttpClient. We’ll be using it to fetch the access token.

OkHttpClient client = new OkHttpClient();

You’ll also need a Base64 encoded authentication string containing your CLIENT_ID and a blank password, separated by a :.

String authString = CLIENT_ID + ":";
String encodedAuthString = Base64.encodeToString(authString.getBytes(),
        Base64.NO_WRAP);

You can now build a Request object to connect to the ACCESS_TOKEN_URL. You must pass the encodedAuthString as a header of the request. It’s also a good idea to include a custom User-Agent header.

You must pass the code as POST data.

Accordingly, add the following code to the method:

Request request = new Request.Builder()
        .addHeader("User-Agent", "Sample App")
        .addHeader("Authorization", "Basic " + encodedAuthString)
        .url(ACCESS_TOKEN_URL)
        .post(RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"),
                "grant_type=authorization_code&code=" + code +
                "&redirect_uri=" + REDIRECT_URI))
        .build();

Finally, use the newCall() method of the OkHttpClient object to actually execute the request. You can use the enqueue() function to make sure that the request is asynchronous. If there are no errors, in the callback object’s onResponse() method, you’ll get a JSON string containing both the access token and the refresh token.

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        Log.e(TAG, "ERROR: " + e);
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        String json = response.body().string();

        JSONObject data = null;
        try {
            data = new JSONObject(json);
            String accessToken = data.optString("access_token");
            String refreshToken = data.optString("refresh_token");

            Log.d(TAG, "Access Token = " + accessToken);
            Log.d(TAG, "Refresh Token = " + refreshToken);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
});

Conclusion

You now know how to use Reddit’s OAuth 2.0 flow. The access token is valid only for an hour. Therefore, don’t forget to use the refresh token to get a new access token every now and then.

If you found this article useful, please share it with your friends and colleagues!