Android Client-side OAUTH 9

Posted by Don 24 Jan 2009 at 07:37PM

OAUTH is a system for allowing users to manage third-party access to protected resources on a trusted provider. The OAUTH provider in this case is a Ruby on Rails web application that records location data. The "third-party" is an Android mobile application playing the role of the OAUTH consumer. The cell phone owner is the user that gives permission to the provider to allow the third party, here after called the consumer, to access protected resources.

Android Client-side OAUTH

Step 0: Add the OAUTH java library to your android app

Add the jar file from the Java OAUTH project to the lib directory in your android application. Then editing the properties of the project in Eclipse to add a jar file to the classpath.

$ cd ANDROID_PROJECT
$ mkdir lib; cd lib
$ svn export http://oauth.googlecode.com/svn/code/maven/net/oauth/oauth-core/20090121/oauth-core-20090121.jar
Step 1: Acquire a consumer token

A consumer token consists of a key and secret and identifies the consumer software to the provider. Acquiring this token is beyond the scope of OAUTH and each provider does it differently. The simplest implementation is to create one token that all consumer software uses. It means the provider will not be able to differentiate between different consumer software but in this case, the android app is the only consumer. The value of the token can be put in the OAUTH discovery document.

If you are using someone else's OAUTH service, you'll have to go through their steps for getting a consumer token. The token remains the same for the life of the software.

String consumerKey = "icecondor-nest-"+ICECONDOR_VERSION;
String consumerSecret = "";
Step 2: Acquire a request token

A request token is a unique concept in OAUTH. You have to ask permission to ask for permission. The provider will have a URL for acquiring a request token. You have to hard code this or use OAUTH discovery to find this URL.

public static OAuthServiceProvider defaultProvider() {
(... load oauth request/authorization/access URLs)
    OAuthServiceProvider provider = new OAuthServiceProvider(
                        request_url, 
                        authorization_url, 
                        access_url);
    return provider;
}

public OAuthAccessor defaultAccessor() {
    String callbackUrl = "icecondor-android-app:///";
    OAuthServiceProvider provider =  defaultProvider();
    OAuthConsumer consumer = new OAuthConsumer(callbackUrl, 
                            consumerKey,
                            consumerSecret, 
                            provider);
    OAuthAccessor accessor = new OAuthAccessor(consumer);
    return accessor;
}

Step 3: Have the user authorize the request token

Use the authorization URL to send the user to the provider's website via a web browser, along with the request token and a callback URL. The user then informs the provider that the request is approved.

OAuthAccessor client = defaultAccessor();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(
      Uri.parse(
           client.consumer.serviceProvider.userAuthorizationURL+
           "?oauth_token="+client.requestToken+
           "&oauth_callback="+client.consumer.callbackURL));
startActivity(i);
Step 4: The user returns to the android app

Once the user has granted permission to the request token, the provider's web site will send the user back to the android app using the callback url. Android apps can register a "protocol" that can be used when constructing URLs on web pages. The android browser will handle URLs with a registered protocol and redirect the user to that android application. I choose the protocol of "icecondor-android-app" which makes a url that looks like "icecondor-android-app:///". To register a protocol in your android app, add an extra block to the AndroidManifest.xml.

<manifest>
 <application>
   <activity>
    <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:scheme="icecondor-android-app"/>
     </intent-filter>
   </activity>
 </application>
</manifest>
Step 5: Extract the request token

The callback URL is modified to contain the request token. Its the same request token that was sent to the provider's web site, but since the app has restarted, its easier to use the token in the URL than to remember it locally. At the startup of the android app, use the Intent that caused the application to launch - it will contain extra data. The extra data is the URL the provider generated to return the user to the app.

public void onResume() {
    // extract the OAUTH access token if it exists
    Uri uri = this.getIntent().getData();
    if(uri != null) {
      String blessed_request_token = uri.getQueryParameter("oauth_token");
      String blessed_request_secret = uri.getQueryParameter("oauth_token_secret");
        // See step 6
    }
}  
Step 6: Convert the request token to an access token

The access token will allow us to make requests to the service provider.

access_token = convertToAccessTokenAndSecret(
                        blessed_request_token, 
                        blessed_request_secret);
setDefaultAccessToken(access_token[0]); 
setDefaultAccessSecret(access_token[1]); 

public static String[] convertToAccessTokenAndSecret(
                    String request_token, String request_secret) {
 ArrayList<Map.Entry<String, String>> params = 
                    new ArrayList<Map.Entry<String, String>>();
 OAuthClient oclient = new OAuthClient(new HttpClient4());
 OAuthAccessor accessor = defaultAccessor();
 accessor.accessToken = request_token;
 accessor.tokenSecret = request_secret;
 try {
  OAuthMessage omessage = oclient.invoke(accessor, "POST", 
                 accessor.consumer.serviceProvider.accessTokenURL, 
                 params);
  return new String[] {omessage.getParameter("oauth_token"),
                 omessage.getParameter("oauth_token_secret")};

 } ...
}
Step 7: Access the protected resource

This step is very similar to step 6. The access token is used instead of the request token.

ArrayList<Map.Entry<String, String>> params = 
          new ArrayList<Map.Entry<String, String>>();
addPostParameters(params, your_application_data); 
OAuthClient oclient = new OAuthClient(new HttpClient4());
OAuthAccessor accessor = defaultAccessor();
accessor.accessToken = getDefaultAccessToken();
accessor.tokenSecret = getDefaultAccessSecret();
OAuthMessage omessage = oclient.invoke(accessor, 
          "POST",  access_url, params);

Much thanks goes to Sean Sullivan who trailblazed the OAUTH on Android process with the jFireeagle android app, and gave me help and advice in my own project.

Comments

Leave a comment

  1. Alberto 13 Apr 2009 at 10:07AM
    Good work, thanks. Can you explain me what to writte exactly on the scheme? () I use the folder where is the manifest but it not work... Can you help me?
  2. Matthias 02 May 2009 at 05:56AM
    Hi, in case anyone is interested: I have written an OAuth library for Java that works pretty well with Android. Check it out at http://code.google.com/p/oauth-signpost
  3. irving 27 Jul 2009 at 11:44PM
    hi . thanks your log .but i am a new android develper .so do'nt know how to do .can you send the code to my email,or share the code in this page . thanks vary much.
  4. eddie 15 Sep 2009 at 07:42AM
    Thanks! This looks exactly what i have been searching for to get the browser to recognize my scheme callback. Can't wait to get home and try it.
  5. yefei 25 Oct 2009 at 10:50PM
    Hey, I followed the exactly process u provided ... but I get client.requestToken = null in step 3. it's not twitter api but some other api following the oauth rule Hope you can help
  6. Jason 04 Dec 2009 at 11:34AM
    Looks like you might be missing this as the last lines of step 2:
    try {
    client.getRequestToken(accessor);
    } catch (IOException e) {
    e.printStackTrace();
    } catch (OAuthException e) {
    e.printStackTrace();
    } catch (URISyntaxException e) {
    e.printStackTrace();
    }
    and in "this" code example you need to return the accessor
  7. John Kristian 02 Mar 2010 at 03:59PM
    To access a protected resource, this is a little clumsy:
    params.add(new OAuth.Parameter("oauth_token", LocationRepositoriesSqlite.getDefaultAccessToken(this)));
    This is usually better:
    accessor.accessToken = LocationRepositoriesSqlite.getDefaultAccessToken(this);
    It would be nice if LocationRepositoriesSqlite.defaultAccessor did this.
  8. SI 23 Mar 2010 at 10:34AM
    Hi, we had used above code, but when we get oauth_verifier and oauth_token using below code, it will return null. protected void onResume() { super.onResume(); Uri uri = this.getIntent().getData(); if (uri != null) { String oauth_token = uri.getQueryParameter("oauth_token"); String oauth_verifier = uri.getQueryParameter("oauth_verifier"); } } So please can you help us if you have any solution.
  9. Don 23 Dec 2010 at 12:44PM
    Two years later, this post still gets hits from time to time. I filled in parts of the code to make it easier to read, fixed a few errors, and improved the syntax coloring. Note the code should be treated as pseudocode, its been extracted and genericized from the icecondor-client-android project. It does not run on its own, but can be used as a guide for new implementations. Look on github if you want to see the full code.
Comments