Quantcast
Channel: Do it yourself Android » OAuth Signpost
Viewing all articles
Browse latest Browse all 4

OAuth in Android using the Google APIs Client Library for Java

$
0
0
Today we’ll be looking at the Google APIs Client Library for Java.  The API is provided by Google, and is a flexible, efficient, and powerful Java client library for accessing any HTTP-based API’s on the web. According to Google, it is the recommended library for accessing Google API’s based on REST or JSON-RPC. One of the nice things about this library is that it fully supports the Android environment out of the box. So we’ll focus on those features in this article. oauth-android-logo

To avoid confusion, Google offers the following APIs (the first one being the topic of this post, and compatible with the Android platform) :

Unfortunately, there are no samples available that perform the OAuth dance in Android using this library, so I thought I’d write one myself. The code for this article can be found in the AndroidOauthGoogleApiJavaClient repository

The first thing we need to do is download the library. You can use the link above to download the library as a JAR file.

The library supports the following authentication mechanisms

We’ll start by looking at the buzz-json-oauth-sample project, and see if we can port it to Android. The sample is a standalone java application that uses OAuth to let the user authorize the application to show his/her Buzz posts. It follows the standard OAuth flow. As this is not a web-application, the sample needs a way to capture the OAuth tokens after the user has granted access. It does so by starting an embedded Jetty server to host a callback-URL, needed to capture the oauth access token.

In short, when we launch the application, the following happens :

  • Setup the JSON/HTTP transport
  • Authorize the user using OAuth
    • The application launches a browser to initiate the OAuth dance
    • After the user has authorized access, a callback is done to a local jetty
    • The landing page on the local jetty allows us to capture the oauth access token
  • Perform authorized API calls to Google Buzz

Our Android based implementation will follow the same steps, but without the local jetty callback offcourse. With Android we can use the intent mechanism to capture the oauth access token, so there’s no need to start a local HTTP server.

Setting up the transport

The Google API Client library for Java abstracts the HTTP transport through an com.google.api.client.http.HttpTransport object. As noted in the documentation, the recommended concrete implementation HTTP transport library to use depends on what environment you are running in. As we’re writing an Android application here, we’ll be using the com.google.api.client.apache.ApacheHttpTransport.

We’ll define our Transport in a Util class

	static HttpTransport newTransport() {
		HttpTransport result = new ApacheHttpTransport();
		GoogleUtils.useMethodOverride(result);
		GoogleHeaders headers = new GoogleHeaders();
		headers.setApplicationName("Google-BuzzSample/1.0");
		result.defaultHeaders = headers;
		return result;
	}

Next, we need to add an HTTP response content parser to the transport, so that we can parse the response coming the transport.

	private static void setUpTransport() {
		JsonCParser parser = new JsonCParser();
		parser.jsonFactory = JSON_FACTORY;
		TRANSPORT.addParser(parser);
	}

Signing requests

OAuth is all about retrieving tokens using signed requests. By obtaining an OAuth access token at the final step of the OAuth flow, the application can do authorized API calls on behalf of the user. As the OAuth specification mandates that all requests are properly signed, the signing process is very important in OAuth.

The OAuthHmacSigner object is the object responsible for signing requests, and as such is very important in the OAuth flow, as it is required to sign all requests so that they can pass as authorized requests.
The OAuthHmacSigner is initialized like this :

			signer = new OAuthHmacSigner();
			signer.clientSharedSecret = Constants.CONSUMER_SECRET;

At the start of the OAuth flow, when the user hasn’t authorized access yet, the signer object contains

Key Value
clientsharedsecret anonymous
tokensharedsecret EMPTY

Notice how the tokensharedsecret is not available in the start of the OAuth dance. This value will be filed in after we’ve fetched the Request Token.

Request Token

Our Android activity uses the following code to launch the OAuth flow :

		// Launch the OAuth flow to get an access token required to do authorized API calls.
		// When the OAuth flow finishes, we redirect to this Activity to perform the API call.
		launchOauth.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				startActivity(new Intent().setClass(v.getContext(),
						PrepareRequestTokenActivity.class));
			}
		});

As you can see, a seperate activity (PrepareRequestTokenActivity) is used to fetch the actual request token. This activity does nothing more then initializing our Signer object, and starting an asyncTask called OAuthRequestTokenTask.

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
    	try {
			signer = new OAuthHmacSigner();
			signer.clientSharedSecret = Constants.CONSUMER_SECRET;
    	} catch (Exception e) {
    		Log.e(TAG, "Error creating consumer / provider",e);
		}
        Log.i(TAG, "Starting task to retrieve request token.");
		new OAuthRequestTokenTask(this,signer).execute();
	}

We start the OAuth dance by fetching a Request Token. This is done in the OAuthRequestTokenTask by creating a GoogleOAuthGetTemporaryToken. This object will allow us to fetch our initial token, the Request Token.

The GoogleOAuthGetTemporaryToken represents a generic Google OAuth 1.0a URL to request a temporary credentials token (also known as the “request token”) from the Google Authorization server.

			GoogleOAuthGetTemporaryToken temporaryToken = new GoogleOAuthGetTemporaryToken();
			temporaryToken.transport = Util.AUTH_TRANSPORT;

			temporaryToken.signer = signer;
			temporaryToken.consumerKey = Constants.CONSUMER_KEY;
			temporaryToken.scope = Constants.SCOPE;
			temporaryToken.displayName = Util.APP_DESCRIPTION;
			temporaryToken.callback = Constants.OAUTH_CALLBACK_URL;
			OAuthCredentialsResponse tempCredentials = temporaryToken.execute();

The GoogleOAuthGetTemporaryToken object contains

Object Value
Our signer object com.google.api.client.auth.oauth.OAuthHmacSigner
Consumer Key anonymous
Scope https://www.googleapis.com/auth/buzz
Displayname Buzz API Java Client Sample
Callback URL x-oauthflow://callback

The GoogleOAuthGetTemporaryToken toString() method looks like this :

https://www.google.com/accounts/OAuthGetRequestToken?scope=https://www.googleapis.com/auth/buzz&xoauth_displayname=Buzz%20API%20Java%20Client%20Sample

In order to retrieve a request token, we call the GoogleOAuthGetTemporaryToken execute() method, like this :

OAuthCredentialsResponse tempCredentials = temporaryToken.execute();

By calling the execute() method, Google verifies that the requesting application has been registered with Google or is using an approved signature (in the case of installed applications). The temporary token acquired with this request is found in OAuthCredentialsResponse This temporary token is used in GoogleOAuthAuthorizeTemporaryTokenUrl to direct the end user to a Google Accounts web page to allow the end user to authorize the temporary token (see next step – Authorize Token).

The exeucte() method returns a com.google.api.client.auth.oauth.OAuthCredentialsResponse, containing our request token, and token secret.

Key Value
token 4/hFOwXawX4WNVVcfPgYSWlidypOAL
tokensecret skaOU3r8uhovUSuTyFhsscLV

These 2 values are very important. We’ll to asign :

  • the tokenSecret (shared-secret for use with “HMAC-SHA1” signature algorithm) onto the signer object we defined earlier.
  • the token to our OAuthAuthorizeTemporaryTokenUrl object that we’ll create now.

So after having retrieved the request token, we can assign the tokenSecret to our signer object. Our signer object now contains this :

Key Value
clientsharedsecret anonymous
tokensharedsecret skaOU3r8uhovUSuTyFhsscLV

Authorize Token

Next step in the OAuth dance is to retrieve the authorize token. In order to do that, we create a OAuthAuthorizeTemporaryTokenUrl. This object is an OAuth 1.0a URL builder for an authorization web page to allow the end user to authorize the temporary token.

This object contains the following properties.

			OAuthAuthorizeTemporaryTokenUrl authorizeUrl = new OAuthAuthorizeTemporaryTokenUrl(Constants.AUTHORIZE_URL);
			authorizeUrl.set("scope", temporaryToken.scope);
			authorizeUrl.set("domain", Constants.CONSUMER_KEY);

			authorizeUrl.set("xoauth_displayname", Util.APP_DESCRIPTION);
			authorizeUrl.temporaryToken = tempToken = tempCredentials.token;

The scope and the domain are mandatory fields we need to set on the OAuthAuthorizeTemporaryTokenUrl object. API specific values need to be set on this object. For example, the Google Latitude API supports the location=”all” and granulariy=”best” fields on the authorization endpoint. As such, the can be set like this:

authorizeUrl.put("location", "all");
authorizeUrl.put("granularity", "best");

But as we’re using the Google Buzz API here, you can see, we set the following properties :

Object Value
Domain anonymous
Scope https://www.googleapis.com/auth/buzz
xoauth_displayname Buzz API Java Client Sample
temporaryToken 4/hFOwXawX4WNVVcfPgYSWlidypOAL

The OAuthAuthorizeTemporaryTokenUrl toString looks like this :


https://www.google.com/buzz/api/auth/OAuthAuthorizeToken?scope=https://www.googleapis.com/auth/buzz&domain=anonymous&xoauth_displayname=Buzz%20API%20Java%20Client%20Sample

To retrieve the actual Authorization URL, we call the authorizeUrl.build() method. This gives us the actual Authorization URL we’ll need to show the user in a browser.


https://www.google.com/buzz/api/auth/OAuthAuthorizeToken?oauth_token=4/hFOwXawX4WNVVcfPgYSWlidypOAL&scope=https://www.googleapis.com/auth/buzz&domain=anonymous&xoauth_displayname=Buzz%20API%20Java%20Client%20Sample

In our Android application, we’ll use this URL to pop a browser, as at this point user interaction is required in order for him/her to authorize the request.
This is done using the following code :

			Log.i(TAG, "Popping a browser with the authorize URL : " + url);
			Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)).setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_FROM_BACKGROUND);
			context.startActivity(intent);

After the user has granted access, he has authorized our temporary token. After this authorization, we can capturing the following 2 important fields

  • oauth_verifier
  • oauth_token

These parameters are embedded in the URL after the user has granted access. In Android, capturing these parameters is done via the callback URL we specified in the beginning. This callback URL is the URL that is shown in the browser after the user has authorized the request. In our Android application, we have defined an activity with an intent-filter, listening for that callback URL.

		<activity android:name=".oauth.PrepareRequestTokenActivity" android:launchMode="singleTask">>
			<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="x-oauthflow" android:host="callback" />
			</intent-filter>
		</activity>

This means that when our callback URL (x-oauthflow://callback) is shown in the browser, our Activity kicks in, and its onNewIntent method is called. In this method, we capture the oauth_token and oauth_verifier like his :

	/**
	 * Called when the OAuthRequestTokenTask finishes (user has authorized the request token).
	 * The callback URL will be intercepted here so we can fetch the token and token secret.
	 */
	@Override
	public void onNewIntent(Intent intent) {
		super.onNewIntent(intent);
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
		final Uri uri = intent.getData();
		if (uri != null && uri.getScheme().equals(Constants.OAUTH_CALLBACK_SCHEME)) {
			Log.i(TAG, "Callback received : " + uri);
			new RetrieveAccessTokenTask(this,signer,prefs).execute(uri);
			finish();
		}
	}

As you can see, we capture the URL, and pass it on to our RetrieveAccessTokenTask, where we can actually fetch our access token.

Access token

Capturing the 2 fields (oauth_verifier and oauth_token) is done in the RetrieveAccessTokenTask. We retrieve the 2 parameters from the URL

[sourcode language=”java”]
String requestToken = uri.getQueryParameter(“oauth_token”);
String verifier = uri.getQueryParameter(“oauth_verifier”);
[/sourcecode]

In order to retrieve the actual access token that we’ll need to perform the authorized API calls, we need an GoogleOAuthGetAccessToken object that contains both the oauth_token and oauth_verifier.

		GoogleOAuthGetAccessToken accessToken = new GoogleOAuthGetAccessToken();
		accessToken.transport = Util.AUTH_TRANSPORT;
		accessToken.temporaryToken = requestToken;
		accessToken.signer = signer;
		accessToken.consumerKey = Constants.CONSUMER_KEY;
		accessToken.verifier = verifier;

The GoogleOAuthGetAccessToken object contains

Object Value
temporaryToken 4/hFOwXawX4WNVVcfPgYSWlidypOAL
Our signer object com.google.api.client.auth.oauth.OAuthHmacSigner
Consumer Key anonymous
Verifier jAsUb68lGmDO8P1sivfgGLz6

In order to get the actual access token (token and tokenSecret), we execute the following call to retrieve a OAuthCredentialsResponse object :

credentials = accessToken.execute();

The OAuthCredentialsResponse returned here contains

Key Value
token 1/PeJEgwmhgAbQyNTB3knWg5k1ABPBgT7DGC6ZNvkcvMQ
tokensharedsecret /m+kCfGms8xI/Zm88oy5OGMC

We store the credentials in our Android Shared Preferences, so that we don’t need to force the user to authorize the request again.

		 final Editor edit = prefs.edit();
		 edit.putString(Constants.PREF_KEY_OAUTH_TOKEN, credentials.token);
		 edit.putString(Constants.PREF_KEY_OAUTH_TOKEN_SECRET, credentials.tokenSecret);
		 edit.commit();

Making API calls

In order to make an authorized API call, we’ll do the following

  • Retrieve the access token and token secret from our shared preferences
  • Create a signer object, containing our token secret
  • Create an authorizer object, containing our signer and access token
  • Tell the authorizer to sign our requests

These steps can be seen in the code snippet below :

		String token = prefs.getString(Constants.PREF_KEY_OAUTH_TOKEN, "");
		String secret = prefs.getString(Constants.PREF_KEY_OAUTH_TOKEN_SECRET, "");

		OAuthHmacSigner signer = new OAuthHmacSigner();
		signer.clientSharedSecret = Constants.CONSUMER_SECRET;
		signer.tokenSharedSecret = secret;
		OAuthParameters authorizer = new OAuthParameters();
		authorizer.consumerKey = Constants.CONSUMER_KEY;
		authorizer.signer = signer;
		authorizer.token = token;
		authorizer.signRequestsUsingAuthorizationHeader(Util.TRANSPORT);

The signer contains :

Key Value
clientsharedsecret anonymous
tokensharedsecret /m+kCfGms8xI/Zm88oy5OGMC

The authorizer contains :

Key Value
consumerKey anonymous
Our signer object com.google.api.client.auth.oauth.OAuthHmacSigner
token 1/PeJEgwmhgAbQyNTB3knWg5k1ABPBgT7DGC6ZNvkcvMQ

The actual API calls are done through the Google Buzz model provided by the sample apps, contained in the com.google.api.client.sample.buzz.model.

For example, retrieving a list of Buzz posts is done through the BuzzActivityFeed object:

BuzzActivityFeed feed = BuzzActivityFeed.list();

The list is implemented by executing a GET request through the transport (meaning it’s properly signed), and parsing the respone using the JSON parser to return it as a BuzzActivityFeed item.

  public static BuzzActivityFeed list() throws IOException {
    HttpRequest request = Util.TRANSPORT.buildGetRequest();
    request.url = BuzzUrl.forMyActivityFeed();
    return request.execute().parseAs(BuzzActivityFeed.class);
  }

References


Viewing all articles
Browse latest Browse all 4

Latest Images

Trending Articles





Latest Images