In this article, I’m going to show you how you can implement an OAuth 2.0 flow in Android. We’ll be using
In the sample application, we’re going to execute 1 authorized API call to the Latitude API. The call will return the current location of the user. |
Image may be NSFW. Clik here to view. ![]() |
The sample application
In the sample application, all dependencies are included in the lib folder, so you should be able to checkout everything from Git, import it in your Eclipse IDE, change your Oauth2 properties in the OAuth2ClientCredentials class, and run the project on your emulator or phone.
The project structure should look like this after a succesful checkout / build :
Image may be NSFW.
Clik here to view.
The project depends on the following libraries :
- lib/commons-codec-1.3.jar
- lib/commons-logging-1.1.1.jar
- lib/google-api-client-1.4.1-beta.jar
- lib/google-api-client-googleapis-1.4.1-beta.jar
- lib/google-api-services-latitude-v1-1.1.0-beta.jar
- lib/gson-1.6.jar
- lib/guava-r09.jar
- lib/httpclient-4.0.3.jar
- lib/httpcore-4.0.1.jar
- lib/jackson-core-asl-1.6.7.jar
Google API client for Java
Google APIs Client Library for Java makes it very easy to interact with various Google APIs. The library has a set of generated client libraries that hide a lot of the complexities when interacting with Google APIs.
As an example, to get the current location of the user using the Latitude API, all it takes is the following 3 lines of code.
Latitude latitude = new Latitude(transport, accessProtectedResource, jsonFactory); latitude.apiKey=OAuth2ClientCredentials.API_KEY; LatitudeCurrentlocationResourceJson currentLocation = latitude.currentLocation.get().execute();
What the code above does is the following
- Initialize a Latitude service definition object, the main gateway to the Latitude API. The object is initiazed with an HTTP Transport, an HTTP request initializer and a JSON factory.
- We specify an API key on the Latitude service defintion object, so we can track the API usage in the Google APIs console.
- We execute a GET request on the currentLocation endpoint.
In order to initialize the Latitude service definition,
- we specify an HTTP transport, as the Latitude API like all Google APIs is a REST based API, where all communication is done over HTTP.
- we specify an HTTP request initializer, as we need to ensure that the API calls are properly authorized. (meaning that the proper authorization headers get filled in with our OAutn 2.0 token).
- we specify a JSON Factory object, so that the responses coming back from the API can be serialized into a clean java based model.
So in short, as a developer, there’s no need to write plumbing code to create HTTP request objects, parse responses, making sure everything is properly signed…. all of that plumbing is handled by the Google API client for Java library, and the Latitude generate client library.
However, before we can actually start making these calls, we need to make sure we have an OAuth 2.0 access token. If we attempt to call the API in a non-secured way, we’ll get the following exception.
com.google.api.client.http.HttpResponseException: 401 Unauthorized at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:380) at com.google.api.services.latitude.Latitude$RemoteRequest.execute(Latitude.java:550) at com.google.api.services.latitude.Latitude$CurrentLocation$Get.executeUnparsed(Latitude.java:222) at com.google.api.services.latitude.Latitude$CurrentLocation$Get.execute(Latitude.java:207) at com.ecs.android.sample.oauth2.AndroidOauthGoogleApiJavaClient.getCurrentLocation(AndroidOauthGoogleApiJavaClient.java:107) at com.ecs.android.sample.oauth2.AndroidOauthGoogleApiJavaClient.performApiCall(AndroidOauthGoogleApiJavaClient.java:80)
Preparing your app for OAuth 2.0
Before you can run the application, you’ll need to get a hold of an OAuth client ID, and an API key for Google Latitude. Both things can be retrieved from the Google API console. The Google API console allows you to define one or more projects. Create a project, and make sure that for “Latitude API”, the status switch is flipped to the “ON” position.
Next, click on “API Access”. You should see the following screen :
Click on “Create an OAuth 2.0 Client ID…”. Choose a product name, and optionally load up an image and click “Next”. (the product name and image will be shown to the user when your application is requesting him to authorize access).
Make sure you select “Installed application” and click “Create client ID”.
You should see the following summary
This summary contains 2 important items
- The OAuth Client ID / Secret
- The API key
You need the Oauth Client ID to setup your Oauth 2.0 communication. The API key is used to track the Latitude API usage in the console when clicking on the Reports link.
Before running the application, make sure you specify the Oauth 2.0 client ID, client secret and API key in the OAuth2ClientCredentials class
The OAuth 2.0 flow in Android
The first thing we need to before we can make a call to the Latitude API is to obtain an OAuth 2.0 access token. This token is obtained through a series of HTTP interactions between the application and the service provider (Google in this case). The first step is to present the user with an authorization screen, allowing him to authorize our application to access his Latitude API.
The authorization web page
This authorizationUrl can be generated using the GoogleAuthorizationRequestUrl object, provided by the Google API client for Java.
The GoogleAuthorizationRequestUrl is a Google extension to the OAuth 2.0 (draft 10) URL builder for an authorization web page to allow the end user to authorize the application to access their protected resources.
String authorizationUrl = new GoogleAuthorizationRequestUrl(OAuth2ClientCredentials.CLIENT_ID, OAuth2ClientCredentials.OAUTH_CALLBACK_URL, OAuth2ClientCredentials.SCOPE).build();
What’s important to note here is that we need to provide our OAuth 2.0 client ID (that we can found in the APIs console), a callback URL (after the user has authorized the request, Google will issue a redirect to this URL), and the
autorization scope.
The Latitude API provides the following OAuth 2.0 based scopes :
https://www.googleapis.com/auth/latitude.current.city
Access to current location at city granularity.
https://www.googleapis.com/auth/latitude.current.best
Access to best-available current location.
https://www.googleapis.com/auth/latitude.all.city
Access to current location and location history at city granularity.
https://www.googleapis.com/auth/latitude.all.best
Access to best-available current and past locations.
The generated URL that looks like this:
https://accounts.google.com/o/oauth2/auth?client_id=1021221231376.apps.googleusercontent.com&redirect_uri=http://localhost&response_type=code&scope=https://www.googleapis.com/auth/latitude.all.best
The page looks like this:
Image may be NSFW.
Clik here to view.
Notice how the image and product name is shown on the authorization page.
In our Android sample application, we’ll load up this URL in a WebView. The reason why I’ve opted to use a WebView is because we need a hook somewhere to intercept the page when the user authorizes the request. Google OAuth 2.0 has some limitations regarding the redirect URIs. In a previous article, where I showed you how to implement the OAuth 1.0 flow in Android, we used a custom scheme in our Oauth redirect URL (xoauth://callback). With OAuth 2.0 this is no longer possible. In our case, we’re using http://localhost as a callback, and we’ll use Webview to intercept this page, so that we can retrieve the code.
There are other advantages of using a Webview. For example, the Webview doesn’t have an address bar, so the user cannot navigate away from the page. Also, the Webview doesn’t interfere with your browser app, something you will have when you pop the browser app from your Android application.
We start by creating a WebView component, and putting it as the main content of the activity.
WebView webview = new WebView(this); webview.getSettings().setJavaScriptEnabled(true); webview.setVisibility(View.VISIBLE); setContentView(webview);
We create a WebViewClient, needed to have a hook when the user has authorized the request, and we need to intercept the code.
In this code, we check if the URL loaded into the webview is our redirect URL (simple startswith check). If this is the case, there can be 2 options :
The user authorized the request, meaning a code request parameter will be present in the URL. If this is the case, we retrieve the code from the URL, and create a GoogleAuthorizationCodeGrant object.
We pass on our transport, a JSON factory, our OAuth 2.0 client ID and client secret, our code and our callback URL. When we execute the GoogleAuthorizationCodeGrant, we get a AccessTokenResponse that contains our OAuth 2.0 access token and refresh token.
We store the token response in our shared preferences, hide the webview (hack to ensure the user doesn’t see the redirect URL being loaded in the webview, as we only need it to fetch the code, and not to display the URL), and start our main activity again. (In the main activity, we perform the Latitude API call, only this this time we’ll have a valid access token.
/* WebViewClient must be set BEFORE calling loadUrl! */ webview.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { if (url.startsWith(OAuth2ClientCredentials.OAUTH_CALLBACK_URL)) { try { if (url.indexOf("code=")!=-1) { String code = extractCodeFromUrl(url); AccessTokenResponse accessTokenResponse = new GoogleAuthorizationCodeGrant(new NetHttpTransport(), new JacksonFactory(), OAuth2ClientCredentials.CLIENT_ID, OAuth2ClientCredentials.CLIENT_SECRET, code, OAuth2ClientCredentials.OAUTH_CALLBACK_URL).execute(); CredentialStore credentialStore = new SharedPreferencesCredentialStore(prefs); credentialStore.write(accessTokenResponse); view.setVisibility(View.INVISIBLE); startActivity(new Intent(PrepareRequestTokenActivity.this,AndroidOauthGoogleApiJavaClient.class)); } else if (url.indexOf("error=")!=-1) { new SharedPreferencesCredentialStore(prefs).clearCredentials(); startActivity(new Intent(PrepareRequestTokenActivity.this,AndroidOauthGoogleApiJavaClient.class)); } } catch (IOException e) { e.printStackTrace(); } } System.out.println("onPageFinished : " + url); } });
The following code simply loads up the authorizationUrl into the webview.
String authorizationUrl = new GoogleAuthorizationRequestUrl(OAuth2ClientCredentials.CLIENT_ID, OAuth2ClientCredentials.OAUTH_CALLBACK_URL, OAuth2ClientCredentials.SCOPE).build(); webview.loadUrl(authorizationUrl);
Access / Refresh Tokens
The sample application contains a simple SharedPreferencesCredentialStore class that we’ll use to store the Oauth 2.0 tokens, and retrieve them when we want to make an API call. Once we have the OAuth 2.0 tokens in our shared preferences, there’s no need to pop the Webview again, as the user has already authorized the request.
Executing the API call
The actual code to perform the secured API call can be found here.
private void performApiCall() { try { JsonFactory jsonFactory = new JacksonFactory(); HttpTransport transport = new NetHttpTransport(); CredentialStore credentialStore = new SharedPreferencesCredentialStore(prefs); AccessTokenResponse accessTokenResponse = credentialStore.read(); GoogleAccessProtectedResource accessProtectedResource = new GoogleAccessProtectedResource(accessTokenResponse.accessToken, transport, jsonFactory, OAuth2ClientCredentials.CLIENT_ID, OAuth2ClientCredentials.CLIENT_SECRET, accessTokenResponse.refreshToken); final Latitude latitude = new Latitude(transport, accessProtectedResource, jsonFactory); latitude.apiKey=OAuth2ClientCredentials.API_KEY; LatitudeCurrentlocationResourceJson currentLocation = latitude.currentLocation.get().execute(); String locationAsString = convertLocationToString(currentLocation); textView.setText(locationAsString); } catch (Exception ex) { ex.printStackTrace(); textView.setText("Error occured : " + ex.getMessage()); } }
- We start by creating a JSON Factory and an HTTP transport.
- We retrieve our accessTokenResponse from the credentialStore (were put there when the user authorized access through the WebView).
- We create our Latitude service definition object
- We execute a GET request on the currentLocation, to retrieve a LatitudeCurrentlocationResourceJson object.
- We output the current location on the screen.
Conclusion
I was surprised to see that there was little documentation available on how to properly implement OAuth 2.0 on an Android platform. I have the feeling that the method being outlined here is currently the only way to do it properly. During the last Google I/O, there was a session that covered OAuth 2.0 integration with the Android AccountManager, meaning that you can authorize a user through the AccountManager. That should be more tightly coupled with the Android system, and you’re not forced to go through the Oauth 2.0 web based flow like we did here. Unfortunately, this didn’t seem to work for the LAtitude API. IT is possible to get it up and running with the Google Buzz and Google Tasks API, but it also doesn’t really make a great user experience. It’s also not documented at all, and unclear if Google sees this as the preferred way of using OAuth 2.0 on an Android platform. The talk also mentioned that they are not quite ready with it.
So, to conclude, we’ve implemented the Oauth 2.0 webflow on an Android platform. Thanks to the Google API client for Java, very little code was required to setup the OAuth 2.0 dance, and using the WebView, we’ve ensured that we had a decent user experience, despite the fact that we needed to load up the Google authorization page. This approach can be used for any Google API, and although the Google APIs Client Library for Java is very Google API centric, it can be used with other service providers as well.