Home > OAuth > Oauth 2.0 flow in Android

Oauth 2.0 flow in Android

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.

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 :


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:

OAuth 2.0 authorization screen

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.

References

Categories: OAuth Tags: , ,
  1. asksven
    August 10th, 2011 at 05:05 | #1

    Excellent article and well written sample, a must read for anyone who wants to work with the Latitude API (and other). I have been looking for such information for a while and IMHO google suffers from the lack of clear directions / information in this area.
    One suggestion: as the used libs are changing and can not be used in the current version it would be good to provide them as part of the project for an easier start in compiling/running using the sample (I found that using google-api-services-latitude-v1-1.2.0-beta-SNAPSHOT.jar and google-api-java-client-1.5.0-beta.zip broke the sample and went back to 1.1.1 and 1.4.1 as a starting point).

  2. admin
    August 10th, 2011 at 06:29 | #2

    Thx for the feedback. Good point on the dependencies. I’ll update the article / sample with more information on these dependencies / libraries. I’ll also check what exactly changed with 1.5.0 / 1.2.0 beta.

  3. admin
    August 10th, 2011 at 08:23 | #3

    I’ve updated the sample / post with the libraries in git. You should be able to checkout / build and run.

  4. asksven
    August 10th, 2011 at 17:03 | #4

    that’s cool, thank you very much. I’ll clone again once I have validated the changes I have been doing to update / read my position and testing the effect of using the listener to get my position updated vs. setting alarms and removing the receiver after getting an update. I believe you guessed what I am after: replacing the battery consuming and inexact / delayed updates from the original Latitude App.

  5. admin
    August 11th, 2011 at 06:07 | #5

    Don’t we all :) Checkout https://market.android.com/details?id=com.ecs.latify for my version of Latitude+ :)

    I’ve created a branch for the 1.2.0 / 1.50 beta combo. https://github.com/ddewaele/AndroidOauth2GoogleApiJavaClient/branches/v1_2_0_latitude_library
    The 1.2.0 client lib breaks compatibility with the previous version, and you also need 1.5.0-beta of the code library. Everything is on the branch. You should be OK to just checkout / build / run.
    The thing that broke compatability was that they moved away from public fields to getters/setters. These public fields weren’t marked deprecated so it’s a bit drastic to go ahead and remove them, breaking all compatibility. On the site, there’s also no mention of 1.5.0 beta.

  6. August 16th, 2011 at 08:27 | #6

    Hi!
    I just wanted to tell you, that there is another library, which is also supporting MAC tokens and handles all token- or api requests on its own.
    See: https://github.com/Xotan/OAuth2Android

    regards Christoph

  7. Amit Aswal
    August 25th, 2011 at 08:35 | #7

    Hi, Thanks for nice sample.

    I have used the Google Latitude API from Google API console, its works just fine. Now
    I am using Google book service API as same instruction but i didn’t find the SCOPE of books API as below for Google latitude API.
    SCOPE = “https://www.googleapis.com/auth/latitude.all.best”; (Google latitude API.)
    SCOPE = “….?” (Google Book API)

    String authorizationUrl = new GoogleAuthorizationRequestUrl(OAuth2ClientCredentials.CLIENT_ID, OAuth2ClientCredentials.REDIRECT_URI, OAuth2ClientCredentials.SCOPE).build();

    Can u please tell me where do i find i already get other credentials for Books API but not SCOPE.

    Thanks & Regards
    Amit Aswal

  8. September 1st, 2011 at 03:11 | #9

    Have you tried google api lib to interact with other services that implement oauth2. Is it possible to authorize and use facebook graph api for example?

    • admin
      September 4th, 2011 at 09:35 | #10

      Yes… The Google samples use the google specific subclasses but the library provides generic oauth 1.0a , oauth 2 and http classes that can be used for most if not all oauth enabled service providers.
      For Facebook, as it has a rich API, I prefer to use a java based Facebook API like restFB (http://restfb.com/). as it offers a better abstraction of the Facebook calls. Keep in mind that google api client for java allows you to do the OAuth2 flow, but it doesn’t have client libraries for non-google APIs like Facebook or Twitter. You could create your own Facebook data model based on the Graph API, and use the standard JSON parser in google api client for java to map the JSON responses into your model, but in my case I’m sure restFB does a far better job :) I just use the google api client for java to retrieve the Oauth2 tokens, and feed that to restFB to do my actual Graph calls. I’ll be posting 2 articles soon discussing Google API client for Java with Foursquare and Facebook.

  9. David Rodriguez
    September 9th, 2011 at 08:10 | #11

    Hey, has anyone else gotten a 403 Forbidden when using this?

    Any ideas?

    It throws an exception right in performApiCall. Here is the stack trace:

    http://pastebin.com/USGSv41S

    Thank You!

  10. Nikola
    September 25th, 2011 at 09:30 | #12

    @David Rodriguez I also has got 403 Forbidden.

    Have you solved this problem?

    Thanks.

  11. Aleksander Palka
    October 12th, 2011 at 20:19 | #13

    I am getting the 403 Forbidden error, too. Any ideas why?

  12. Jasmine Rosebrough
    October 28th, 2011 at 00:53 | #14

    Is there a way to use Oauth when creating an application. What I mean is this: I want to create and application where in order to use it, the user will login using their facebook credentials. That is all that I want to use, no other facebook feature will be needed.

  13. Keith Wright
    January 3rd, 2012 at 21:50 | #15

    I was having trouble getting this to work until I removed old authorizations from my email account for the app I was trying it with. https://plus.google.com/settings/general, Authorizing applications & sites, Edit.

  14. Keith Wright
    January 3rd, 2012 at 22:04 | #16

    These two libraries do not seem to be required:
    lib/commons-codec-1.3.jar
    lib/commons-logging-1.1.1.jar

  15. Spike777
    January 12th, 2012 at 15:36 | #17

    Hello, thanks for that excelent tutorial!

    I have a question, once I have an AccessToken, a RefreshToken and the other data stored in the SharedPreferences, how can I check if the token expired or not?

    Regards

  16. asksven
    February 18th, 2012 at 23:19 | #18

    I’d be interested in knowing if you have finally managed to access Latitude with the AccountManager? I have been trying really hard but no success yet.

  17. Daniel Gleim
    February 24th, 2012 at 14:15 | #19

    Hi,
    this is the first page with a running example I can find after two months of desperation.
    My goal is to use the Google Prediction API from an Android Phone (HTC Desire) – as far as I understood this should be possible?

    But I’m getting the same 403 Forbidden as David, any ideas?

    Besides (last but not least) I really really want to thank you for this tutorial! I need prediction in my bachelor exam and I was close to abort that work with a “research showed, its not possible” – now I am optimistic again, to get that thing working!

    Thanks again!

  18. Slawek Rutkowski
    February 29th, 2012 at 14:50 | #20

    Hello,
    I’m struggling with some OAuth2 and Google API issues and I’m very interested in that line of your code:

    extractCodeFromUrl(url)

    What is it? How it works, because I don’t know how are you was able to extract “code” from the URL?

    Thank you in advance!

  19. Francisco
    March 8th, 2012 at 03:40 | #21

    @David Rodriguez, I am also getting a 403 Forbidden when I authorize the app and it redirects to “http://localhost” (back to the app)

  20. March 24th, 2012 at 20:39 | #22

    dont think this works on ICS because network calls blocked on UI thread…

    IMO – the following needs to be threaded so it’s off the UI …

    onPageFinished ..

    GoogleAuthorizationCodeGrant(…

    • admin
      March 25th, 2012 at 13:38 | #23

      Correct… was written pre ICS. Honeycomb blocks them as well (and so they should…). I’ll add it on my TODO list and make the appropriate changes.

  21. numan
    April 2nd, 2012 at 16:23 | #24

    why doesn’t custom scheme solution work for Oauth 2.0 flow?

  22. April 12th, 2012 at 12:16 | #25

    When i clicked on Allow Access button i get forbidden error…..
    Please help me to solve this error
    thanks in adavance

  23. Ujjwal
    April 12th, 2012 at 18:44 | #26

    superb example..

  24. Saha
    April 29th, 2012 at 19:02 | #27

    Hi I am trying to run the sample but I am getting following error during runtime.

    Could not find class ‘com.google.api.client.json.jackson.JacksonFactory’, referenced from method com.ecs.android.sample.oauth2.LatitudeApiSample.performApiCall

    No issues at compile time, build path shows all dependencies resolved correctly per your project structure picture.
    Mean time I have created all necessary Client Key, Secret and API key. Also I have downloaded most recent google-api-java-client-1.8.0-beta. But not sure if there is any use of this in this project since all dependencies are included. But may be I am wrong, can you help?

    Thanks – Saha

  25. Tarek
    May 5th, 2012 at 11:45 | #28

    Thank you for making things clear..
    I tried to make something near to this example but am having problem in the
    GoogleAuthorizationRequestUrl

    although i imported it but eclipse warns me of a deprecated class, i dont know what does that mean a deprecated class in no longer available or something?

    and if yes how can i build google Api authorization url

  26. F8Full
    May 5th, 2012 at 17:36 | #29

    @admin
    Thanks a LOT for that !
    It also contains calls to deprecated stuff (as of 1.8.0), I just finished adapting it, I’ll share the code after cleaning it up (though I target Gingerbread, hence I would have the network calls on UI problem still).
    Thks again.

    F.

  27. beppe
    May 7th, 2012 at 16:43 | #30

    very useful.

    other good thing about using this method is that you can use it on “non official” Android devices like Kindle Fire.

    The account manager solution can’t not be used on those because they doesn’t have any google accounts on it

  28. Nayanesh
    May 14th, 2012 at 06:51 | #31

    I got this error:
    java.net.UnknownHostException: Host is unresolved: http://www.googleapis.com:443

    any idea?

  29. Chandra sekhar
    April 13th, 2013 at 09:55 | #32

    how to get userinfo from google please help me

    while i am running the sample code i am getting 404 error

  1. September 5th, 2011 at 22:36 | #1
  2. January 25th, 2012 at 12:01 | #2