Android & OAuth

Posted By: Kelly Klein

If your Android application needs to implement OAuth there are many options available. The one that I’ve used that I like the best is signpost. It’s simplicity allows it to stay out of your way while you focus on implementing your applications functionality instead of worrying about how OAuth works.

Getting Started

The first thing you’ll have to do is download the signpost library. You’ll need both signpost-commonshttp4 as well as signpost-core. Save these to your hard drive in the lib folder of your project. Now add both libraries to the build path of your project and your ready to go.

Here We Go

We need a class that will implement the signpost library to make calls to the API to retrieve our data. The way this is implemented in Fellowship One for Android is to have a static class that contains all the API calls needed for the application. This class includes class level static objects for CommonsHttpOAuthProvider and DefaultOAuthConsumer to be used throughout the class.

The first thing that needs to happen for an application to use the F1 API is to get the authorized request token and secret. The process to accomplish this can seem a little complicated but is simplified with signpost. The following code starts the process off by allowing the end user to authenticate with the system:


Private static final String consumerKey = "34";
Private static final String consumerSecret = "notTellingYou";
private static final String access_token_url = "https://demo.fellowshiponeapi.com/v1/Tokens/AccessToken";
private static final String login_url = "https://demo.fellowshiponeapi.com/v1/PortalUser/Login";
private static final String request_token_url = "https://demo.fellowshiponeapi.com/v1/Tokens/RequestToken";

public static CommonsHttpOAuthProvider provider = null;
public static DefaultOAuthConsumer consumer = null;

private static void defaultConsumer(Context context) {       
   consumer = new DefaultOAuthConsumer(F1Private.consumerKey, F1Private.consumerSecret);
      
   if (containsSetting(context, "access_token_secret") && containsSetting(context, "access_token")) {
         consumer.setTokenWithSecret(getSetting(context, "access_token"), getSetting(context, "access_token_secret"));
      }
}
    
private static void defaultProvider(Context context) {     
   provider = new CommonsHttpOAuthProvider(request_token_url,
                                         access_token_url,
                                          login_url);
}
  
public static String Login(Context context) {
if (provider == null) {
        defaultProvider(context);
  }
  if (consumer == null) {
        defaultConsumer(context);
  }
  return provider.retrieveRequestToken(consumer, "ft-android-app:///");
}

The important part of this code block is the Login method. Here we instantiate our static CommonsHttpOAuthProvider with the access token, request token and login URLs for the F1 API. We also instantiate our CommonsHttpOAuthConsumer as an empty object (it’s empty in this instance because we don’t have our access token and access token secret yet). Once we have both of these objects we call the retrieveRequestToken method passing our empty consumer object and a callback URL. This method will call the F1 API and get an unauthorized request token for us and then construct a URL that we pass to the users browser so they can enter their credentials:


result = F1OAuth.Login(this);
this.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(result)));

This will open the browser for the user and they will enter their credentials and authorize the application to access Fellowship One and then the F1 API will call the URL that was passed as the callbackURL parameter. But wait. The callbackURL we passed was ft-android-app:///? That’s not a valid URL. What the heck will happen when that URL is called in the browser? I’m glad you asked.

With Android, you can setup intent filters on your activities (if you don’t know what activities are in Android you should probably read this first). With the intent filter you can advertise that your application will answer to certain intents. In this instance, when the Android OS sees the ft-android-app:/// intent it knows that our application wants to be called. So it calls our application and passes whatever data exists in the query string which, in this case, happens to be the authorized request token from the F1 API.


public void onResume() {
super.onResume();

final Uri uri = this.getIntent().getData();

   if (uri != null) {
     final String access_token = uri.getQueryParameter("oauth_token");

       // Get the request token back from the API.         
       F1OAuth.convertToAccessToken(this, access_token);   
   }
}

public static void convertToAccessToken(final Context context, final String request_token) {
  provider.retrieveAccessToken(consumer, request_token);

    // save token and secret.
  Util.setSetting(context, "access_token_secret", consumer.getTokenSecret());
  Util.setSetting(context, "access_token", consumer.getToken());
}

From this code block you can see that we get the oauth_token query string paramater that was sent back from the F1 API from the intent data that was passed to our activity by the Android OS. We now take this authorized request token and use our previous provider object to get the access token from the F1 API. We then save both the access token and secret in shared preferences since they will need to be used for every call me make to the F1 API.

So now that we have successfully authenticated and have our token and secret, we actually need to get data back from the F1 API:


public static String GetPeople(final Context context, final String search)
   String url;
        
   //Format the URL
   url = "https://demo.fellowshiponeapi.com/v1/People/Search.json?include=addresses,communications&includeInactive=False&searchFor=";
   url = url + URLEncoder.encode(search, "utf-8");
      
   URL formattedURL = new URL(url);
   HttpURLConnection request = (HttpURLConnection) formattedURL.openConnection();
request.addRequestProperty("Accept-Encoding", "gzip");
request.setRequestMethod("GET");
     
   if (consumer == null) {
        defaultConsumer(context);
  }
      
   consumer.sign(request);
        
   request.connect();
     
   return Util.InputStreamToString(request.getInputStream());
}

In this method we first construct the URL for a people search with the appropriate parameters. Once we create a request it needs to be signed with our access token and secret. If the consumer object has not yet been created we instantiate it with the saved values of the access token and secret. Once we have a valid consumer, signing the request is as simple as calling the sign method. After that we are ready to call the F1 API and retrieve our data.

Once thing you might notice is this line:


request.addRequestProperty("Accept-Encoding", "gzip");

By adding this request property we are telling the F1 API that we are able to receive back data in a compressed format. While this is not necessary for OAuth it is a best practice to use this feature of the F1 API especially in a mobile client where the network latency may be high. This will allow for a better user experience with your application since the data that is returned from the F1 API will be as small as possible.

To find out more about Android, Signpost and the Fellowship One API:

Android - http://developer.android.com/guide/index.html

Signpost - http://code.google.com/p/oauth-signpost/

F1 API - http://developer.fellowshipone.com/index.php/docs/

Kelly Klein is a Developer for Fellowship Technologies.  He has been with Fellowship Technologies since March 2009.  As a side project with Funkypants Software he developed the Fellowship One for Android application.  He is passionate about serving the Church through technology.

Posted In: Tips,

Comments:
No one has commented yet. Be the first!
Commenting is not available in this channel entry.

Categories:

Previous Posts:


Subscribe to the RSS feed!