ldx's blog

Posted Fri 04 February 2011

Implementing client-side OAuth on Android

OAuth is becoming the de-facto protocol to allow access to your data and services without sharing user password. Effectively all the big names such as Twitter, Google, Yahoo or LinkedIn have already implemented it. There are quite a few libraries and code samples in all the popular programming languages out there to implement OAuth in your desktop, mobile or web application as well.

There are guides for Android too, however most of them are not up to date, accurate or just difficult to comprehend if you are in a hurry. Here we provide a few easy to follow steps with some explanation how it can be done in a straightforward way.

First, a short summary how OAuth works. It is based on cryptography, where

  1. a token and a corresponding secret is acquired by a consumer (a desktop or web application) from a provider (a server in the cloud),
  2. this token is authorized by the user as valid and allowed to access their data and then
  3. the token is upgraded, and this can then be used from then on until it is revoked by same user who authorized it.

The token acquired in the first step is called a request token, this is where you usually specify which service you would like to get access to; it is called scope. The second step is called authorization, after which control can be passed back to the consumer application via a callback. The final token that is received in the third step is called access token. This can be used for a long period of time, it won't expire (but, as mentioned, the user can revoke it any time). It is basically a short string, with a corresponding secret string, and once the application acquired it, it can be used to sign HTTP requests, thus authenticating it for the provider. All three steps have a corresponding URL at the provider, to where an HTTP request is sent to get the token or manipulate it.

If you need further details, there's a good article with API reference at code.google.com, and another very detailed overview with figures here.

We will use the excellent signpost Java library to implement OAuth access to Gmail. Just download at least the signpost-core and signpost-commonshttp4 jars, copy them to the lib/ folder inside your Android project, right click on the project, and under Properties/Java Build Path you can add them to the build path:

Adding the signpost jars to the build path][]

We will implement OAuth support via a helper class called OAuthHelper. The two single most important classes provided by signpost are OAuthConsumer and OAuthProvider; before diving into actual communications, we set these up first:

private OAuthConsumer mConsumer;
private OAuthProvider mProvider;
private String mCallbackUrl;
public OAuthHelper(String consumerKey, String consumerSecret,
String scope, String callbackUrl)
throws UnsupportedEncodingException {
    mConsumer = new CommonsHttpOAuthConsumer(consumerKey, consumerSecret);
    mProvider = new CommonsHttpOAuthProvider(
    "https://www.google.com/accounts/OAuthGetRequestToken?scope="
    + URLEncoder.encode(scope, "utf-8"),
    "https://www.google.com/accounts/OAuthGetAccessToken",
    "https://www.google.com/accounts/OAuthAuthorizeToken?hd=default");
    mProvider.setOAuth10a(true);
    mCallbackUrl = (callbackUrl == null ? OAuth.OUT_OF_BAND : callbackUrl);
}

The consumerKey and consumerSecret strings depend on your consumer application, you can use anonymous for both. Later you might want to register your application at the provider (Google in this case), which will issue a key and secret for your app. To access an user's Gmail inbox the scope is "https://mail.google.com/", the URLs for OAuth are in the constructor of the helper class.

The callbackUrl variable can be used to pass an URL to the provider which will be called once your token is authorized. On Android you can register a special URL scheme to your application, thus the browser will fire up an activity of your app once authorization has been done. E.g. if you would like MyActivity to be called put the following into your app manifest:

<activity android:name="MyActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:scheme="my-activity"></data>
</intent-filter>
</activity>

and pass "my-activity://mywebsite.com/" as the callback URL. This also has the side effect of identifying your application as mywebsite.com at the provider (at least at Google). Your application will get back a verifier code via the callback as a query parameter to the URL, where the query key is "verifier". You will need this later.

As a next step, retrieve the request token:

public String getRequestToken()
throws OAuthMessageSignerException, OAuthNotAuthorizedException,
OAuthExpectationFailedException, OAuthCommunicationException {
    String authUrl = mProvider.retrieveRequestToken(mConsumer,
    mCallbackUrl);
    return authUrl;
}

Once you got back the authentication URL from this method, just start up the browser with it:

try {
    String uri = helper.getRequestToken();
    startActivity(new Intent("android.intent.action.VIEW",
    Uri.parse(uri)));
} catch (...) {
}

In your OnResume() method in MyActivity you can catch the callback and retrieve the verifier, and upgrade your token with it:

String[] token = getVerifier();
if (token != null)
String accessToken[] = getAccessToken(token[1]);
...
private String[] getVerifier() {
    // extract the token if it exists
    Uri uri = this.getIntent().getData();
    if (uri == null) {
        return null;
    }
    String token = uri.getQueryParameter("oauth_token");
    String verifier = uri.getQueryParameter("oauth_verifier");
    return new String[] { token, verifier };
}

In our helper class:

public String[] getAccessToken(String verifier)
throws OAuthMessageSignerException, OAuthNotAuthorizedException,
OAuthExpectationFailedException, OAuthCommunicationException {
    mProvider.retrieveAccessToken(mConsumer, verifier);
    return new String[] {
        mConsumer.getToken(), mConsumer.getTokenSecret()
    };
}

And that's it. Just make sure you save the access token and its secret. You can now use signpost to sign your HTTP queries e.g.

OAuthConsumer consumer = new CommonsHttpOAuthConsumer(accessToken[0],
accessToken[1]);
HttpGet request = new HttpGet(url);
// sign the request
consumer.sign(request);
// send the request
HttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(request);

Happy hacking!

Update: fixed some typos in code snippets.

Category: android, development, java, oauth

Comments