Implementing OAuth 2.0 in an Android app

I have been working on developing an Android native app using the current Web Services APIs from Constant Contact. I took advantage of the “somewhat old” CTCT Webservice Java Client Library in the app, but that library uses the now deprecated Basic Authentication model.

I started to look into re-implementing authentication using the OAuth 2.0 authentication model.

I thought it might be helpful to share what I have found out by showing an Android app to demo OAuth 2.0 implementation in a client. Here’s some background on OAuth. Quoting hueniverse.com, OAuth is a security protocol that enables users to grant third-party access to their web resources without sharing their passwords. Without further ado, here’s what I did.

The Constant Contact developer documentation for Authentication using OAuth 2.0 – Server and Client Flows is actually a good place to start. Scroll down to the section for Client Flow, which is the approach we will use for the mobile app implementation.

There are essentially three steps in the context of using the current Constant Contact Web Services APIs.

1. Within the app, open a browser component that is directed to a login webpage for user to authenticate with his/her Constant Contact account username and password. That will then be followed by a webpage that allows the user to Grant Access to the app.

 

2. Intercept a redirect in the browser component in order to obtain the access token appended to the redirect url.

3. Make a REST call with a POST to obtain the user login name associated with the account. The username is required in order to construct the uri path for the Constant Contact Web Services APIs.

For the first step, the Android Framework has a WebView component that is perfectly suited for the task at hand. The key is to be able to intercept the redirect, and to do this we take advantage of the WebView Client’s callback method shouldOverrideUrlLoading(WebView, String). This is illustrated in the following code snippet for the main activity’s onCreate() life-cycle callback method. For simplicity, some extraneous code has been omitted from the snippet below.

 @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_item_list);
        ...

        // check whether access token already saved
        accessToken = getPreferences(Context.MODE_PRIVATE).getString(SHPREF_KEY_ACCESS_TOKEN, null);
        if (accessToken == null) {

            // need to get access token with OAuth2.0
            ...
            // set up webview for OAuth2 login
            webview.setWebViewClient(new WebViewClient() {
            	@Override
            	public boolean shouldOverrideUrlLoading(WebView view, String url) {
            	    ...
            	    if ( url.startsWith(REDIRECT_URI) ) {

            		// extract OAuth2 access_token appended in url
            		if ( url.indexOf("access_token=") != -1 ) {
            		    accessToken = mExtractToken(url);

            		    // store in default SharedPreferences
            		    Editor e = getPreferences(Context.MODE_PRIVATE).edit();
            		    e.putString(SHPREF_KEY_ACCESS_TOKEN, accessToken);
            		    e.commit();

            		    // spawn worker thread to do api calls to get list of contacts to display
            		    new MyWebservicesAsyncTask().execute(accessToken);
            		}

            		// don't go to redirectUri
            		return true;
            	    }

            	    // load the webpage from url: login and grant access
            	    return super.shouldOverrideUrlLoading(view, url); // return false;
                }
            });

            // do OAuth2 login
            String authorizationUri = mReturnAuthorizationRequestUri();
            webview.loadUrl(authorizationUri);

        } else {

            // have access token, so spawn worker thread to do api calls to get list of contacts to display
            new MyWebservicesAsyncTask().execute(accessToken);
        }
    }

We first check whether the access token has been previously stored. If not, we set up a WebView component. At the end of the setup code, we construct the authorization request url as described in the Client Flow section of the developer documentation, and load that url into the webview component so as to get to the login and the grant access webpages as shown in the images above. After the user performs the login and then the grant access steps, the webview component gets a redirect url, which needs to be intercepted so that the webview component does not actually attempt to reach the redirect url.

For the second step, the above code snippet shows how the webview component is set up with a webview client that overrides the shouldOverrideUrlLoading(WebView, String) callback method; that method gives the Android app a chance to take over control when a new url is about to be loaded in the webview. In the callback method, the url string is compared to the REDIRECT_URI string, which is just the “redirect_uri” described in the Client Flow section. You can specify your redirect_uri string for your API Key by going to Request and Manage API Access Keys For Your Account. As described in the Client Flow section, the access token is appended to the redirect_uri, and can be simply extracted out. We then store the access token in the default Shared Preferences data storage.

With the access token in hand, we now can do the third step (and other useful work) by spawning a worker thread, making use of the Android Framework’s AsyncTask abstract class. In this example, we make the web services calls to retrieve and display the desired contacts in the user’s Constant Contact account. We do this in a worker thread so as not to block the main UI thread of our Android app while fetching the data over the Internet.

If the access token has previously been stored, we can skip the OAuth2 processing and immediately spawn the worker thread to do the web services calls to retrieve the desired contacts to display. Although outside of the scope of a blog on OAuth2, for the sake of completeness, I’ll briefly discuss how we can do that and show the code snippet below for those who are interested.

We declare an inner class MyWebservicesAsyncTask inside the main activity class. This class extends the Android Framework’s AsynTask abstract class. Essentially, the work is done in the doInBackground(String…) method, in which we leverage the CTCTConnection class that is part of the CTCT Webservice Java Client Library. The authenticateOAuth2(String) method is my extension of the library, but all that it does is to do the POST with the access token in order to get the username of the user’s account, as described in the Client Flow section of the developer documentation. We also use the getContactListMembers(String) method in the CTCTConnection class to get a ContactIterator object, and then call getLoadedEntries() on the iterator to get an arraylist of ModelObjects representing the contacts. The current API exposes just two attributes for a contact: name (in “last name, first name” format), and email address. Finally, an arraylist of hashmaps is built to feed a SimpleAdapter, which in turn serves as a source for our list view (or more precisely, the implicit ListView hosted by our ListFragment); setting the adapter for the listview is what the mShowContacts() method mostly does, once the worker thread execution is done. For the sake of completeness, I should also mention here that I did a minor refactoring of the package-private method doGetRequest(String) in the CTCTConnection class in order to set the Authorization header for the HttpGet object to use the access token.

private class MyWebservicesAsyncTask extends AsyncTask {
    	@Override
	protected Boolean doInBackground(String… params) {
	    String accessToken = params[0];

	    CTCTConnection conn = new CTCTConnection();
	    try {
		// authenticate with access_token obtained
		userName = conn.authenticateOAuth2(accessToken);

		// api call to get Contacts for username list id 1
		if (userName != null) {
		    String link = "/ws/customers/"+userName+"/lists/1";

		    ArrayList contacts = conn.getContactListMembers(link).getLoadedEntries();

		    // load cache for display in list
		    contactHashmaps = new ArrayList< HashMap >();
		    HashMap hmap;
		    for (ModelObject mo: contacts) {
			String name = (String) ((Contact) mo).getAttribute(NAME_KEY);
			String email = (String) ((Contact) mo).getAttribute(EMAILADDRESS_KEY);
			hmap = new HashMap();
			hmap.put(NAME_KEY, name);
			hmap.put(EMAILADDRESS_KEY, email);
			contactHashmaps.add(hmap);
		    }

		    return true;
		}
	    } catch (IOException e) {
		e.printStackTrace();
	    } catch (JSONException e) {
		e.printStackTrace();
	    } catch (InvalidCredentialsException e) {
		e.printStackTrace();
	    }

	    return false;
	}

	@Override
	protected void onPostExecute(Boolean result) {
	    // called in UI thread
	    if (result) {
		mShowContacts();
	    }
	    super.onPostExecute(result);
	}
    }

Whew…congrats on making it to this point. Now, some caveats to set expectations. Please note that this is more in the nature of a personal project and blog, and not an official Constant Contact document, so I do not expect our Support folks to be able to help with debugging your app based on any code snippets shared here, nor do I plan to monitor the Blog to answer particular questions (sorry, we all have our day jobs, so I hope you’ll understand). The demo app does run on the Android AVD emulator running API Level 16 (Jelly Bean). I do plan to update this blog on a best efforts basis if there are errors or additional information that could be helpful to a wider audience. But hopefully, the Developer Community could be the self-help forum for questions and discussions on what is shared here.

Finally, for extra credit, notice that the list view in the image above ellipsizes the very long email addresses in the middle. This is done by extending the SimpleAdapter class and overriding its getView(int, View, ViewGroup) method. As this could be a bit tricky and there seems to be not much documentation that I could find, if there is sufficient interest, I might show how to do that in another blog. A good reference source on ListView in general is Romain Guy and Adam Powell’s talk at Google I/O 2010 – The world of ListView.

Source code can be downloaded as an Android project from the android-demo-OAuth20 repository in GitHub.

Note: In the ItemListActivity class, set the CLIENT_ID static member variable to your own developer API Key that you can obtain as described earlier above.

Continue the conversation by sharing your comments here on the blog and by following us on Twitter @CTCT_API

Comments

  1. mcubevmc says:

    Nice Post….. Wana Know More About That Just Log On To:- http://mcube.vmc.in/

Leave a Comment