Quantcast
Viewing all articles
Browse latest Browse all 10

Implementing the OAuth flow in Android

In this post, I’ll be showing you how to use the OAuth signpost library in an Android application. We’ll create a simple application that fetches your Google Contacts using the Google Contacts API, and display the contact names on the screen.
We’ll focus on using the Signpost API in an Android runtime, and implementing the OAuth workflow from the first step (requesting a request token) till the final API call with our access token. We’ll glue the entire OAuth workflow together in our Android application, using different techniques offered by the Android platform. As you can see from the screenshot below, it’s not the most aesthetically pleasing application, but it gets the job done.
Image may be NSFW.
Clik here to view.
oauth-android-logo

Application Overview

If all goes well, our application should look like this :

Image may be NSFW.
Clik here to view.

As our application needs to fetch user data from Google, the user needs to authorize the application in order to fetch his data on his behalf. If the user doesn’t authorize access, the application will not be able to fetch our contacts, and will throw the following exception :

Image may be NSFW.
Clik here to view.

As stated before, in order to avoid this error, we need to authorize the application to fetch this data (act on our behalf), and this is where OAuth comes in the picture. The home screen contains a “Launch OAuth Flow” button that will trigger the OAuth flow we discussed in our OAuth overview post.

When clicking on this button, you’ll be redirected to the google accounts page, where you will be prompted to login to your account (if you haven’t already).

Image may be NSFW.
Clik here to view.

After a succesfull login, Google will state that an application is requesting access to your Google Contacts, and will ask you to authorize or grant this access.

Image may be NSFW.
Clik here to view.

When the user grants access, the application will make an API call to Google, and display the contacts on the screen.

Image may be NSFW.
Clik here to view.

Application internals

Start the OAuth Dance

When we click on the Launch OAuth Flow button, we’re basically launching an activity called PrepareRequestTokenActivity that will be responsible for retrieving the request token.

        Button launchOauth = (Button) findViewById(R.id.btn_launch_oauth);
        Button clearCredentials = (Button) findViewById(R.id.btn_clear_credentials);

        launchOauth.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
            	startActivity(new Intent().setClass(v.getContext(), PrepareRequestTokenActivity.class));
            }
        });

Retrieve Request Token

The PrepareRequestTokenActivity activity will do the following :

  • Prepare the OAuthConsumer with the consumer key and consumer secret
  • Prepare the OAuthProvider by specifying the 3 OAuth endpoints (request , authorize , access)
  • Launch the OAuthRequestTokenTask, responsible for fetching the request token.

This should all look familiar, as we did the exact same thing in the commandline example.

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
    	try {
        	System.setProperty("debug", "true");
    	      this.consumer = new CommonsHttpOAuthConsumer(Constants.CONSUMER_KEY, Constants.CONSUMER_SECRET);
    	        this.provider = new CommonsHttpOAuthProvider(
    	        		Constants.REQUEST_URL  + "?scope=" + URLEncoder.encode(Constants.SCOPE, Constants.ENCODING),
    	        		Constants.ACCESS_URL,
    	        		Constants.AUTHORIZE_URL);
        	} catch (Exception e) {
        		Log.e(TAG, "Error creating consumer / provider",e);
    		}

        Log.i(TAG, "Starting task to retrieve request token.");
		new OAuthRequestTokenTask(this,consumer,provider).execute();
	}

The OAuthRequestTokenTask calls the retrieveRequestToken method on the OAuthProvider. If might recall from our previous hands on example with Signpost that in this step, the Signpost library will fetch a request token from Google, and return the URL that is required to authorize this request token. That authorization URL will then be displayed in a browser, ready for the user to authorize the request.

	/**
	 *
	 * Retrieve the OAuth Request Token and present a browser to the user to authorize the token.
	 *
	 */
	@Override
	protected Void doInBackground(Void... params) {

		try {
			Log.i(TAG, "Retrieving request token from Google servers");
			final String url = provider.retrieveRequestToken(consumer, Constants.OAUTH_CALLBACK_URL);
			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);

		} catch (Exception e) {
			Log.e(TAG, "Error during OAUth retrieve request token", e);
		}

		return null;
	}

So at this point the signpost library has capured the requestToken

Authorize the request token

At this point, the user should login to Google (if not already done), and authorize the request. As you can see from the code snippet above, when requesting the request token, we also provide a callback URL (provider.retrieveRequestToken(consumer, Constants.OAUTH_CALLBACK_URL)).

This callback URL is defined like this x-oauthflow://callback.

Obviously, we want our application to react to this callback, as the URL will contain our authorized token (oauth_token) and the oauth_verifier required to retrieve the access token .

When the user grants his permission, the callback URL is received in the browser, we some kind of interceptor in our application, so an action can be triggered to interpret this callback URL, and extract the oauth_token and oauth_verifier.

This interceptor is implemented on the PrepareRequestTokenActivity using the onNewIntent method.

	/**
	 * Called when the OAuthRequestTokenTask finishes (user has authorized the request token).
	 * The callback URL will be intercepted here.
	 */
	@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);
			Log.i(TAG, "Retrieving Access Token");
			new RetrieveAccessTokenTask(this,consumer,provider,prefs).execute(uri);
			finish();
		}
	}

The fact that the onNewIntent method is called is a result of the manifest , where the activity is configured with a singleTask launchMode. It also has an intent filter to react to our callback url.

Each element in an intent filter can specify a URI and a data type. Here we’re only interested in the URI. There are separate attributes — scheme, host, port, and path — for each part of the URI (scheme://host:port/path) that we can filter against. In our case, we filter on the scheme and the host, as that should be sufficient to identify our callback URL. Remember that the callback URL was defined like this x-oauthflow://callback.

		<activity android:name=".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>

Retrieving the access token

Because our PrepareRequestTokenActivity has a singleTask launchMode AND an intent filter on our callback was specified, the onNewIntent method will be called on our activity as soon as our callback URL is received in the browser. This is the perfect place to continue the OAuth flow. At this point, the user has authorized our request. Using the oauth_verifier, we can now retrieve the access token that will be required to make the API calls. Retrieving the access token is handled by the RetrieveAccessTokenTask

	/**
	 * Retrieve the oauth_verifier, and store the oauth and oauth_token_secret
	 * for future API calls.
	 */
	@Override
	protected Void doInBackground(Uri...params) {
		final Uri uri = params[0];

		final String oauth_verifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);

		try {
			provider.retrieveAccessToken(consumer, oauth_verifier);

			final Editor edit = prefs.edit();
			edit.putString(OAuth.OAUTH_TOKEN, consumer.getToken());
			edit.putString(OAuth.OAUTH_TOKEN_SECRET, consumer.getTokenSecret());
			edit.commit();

			String token = prefs.getString(OAuth.OAUTH_TOKEN, "");
			String secret = prefs.getString(OAuth.OAUTH_TOKEN_SECRET, "");

			consumer.setTokenWithSecret(token, secret);
			context.startActivity(new Intent(context,OAuthFlowApp.class));

			Log.i(TAG, "OAUTH STAGE TWO OK!");

		} catch (Exception e) {
			Log.e(TAG, "OAUTH STAGE TWO ERROR", e);
		}

		return null;
	}
 

What we’re seein here is that the Signpost library is retrieving the access token, and storing it in our shared preferences. Now that we have the acccess token , and token secret, we start our initial activity again.

Image may be NSFW.
Clik here to view.

In our initial activity, we’re doing an API call to the Google Contacts API, and let our OAuthConsumer sign the request :

	private String doGet(String url,OAuthConsumer consumer) throws Exception {
		DefaultHttpClient httpclient = new DefaultHttpClient();
    	HttpGet request = new HttpGet(url);
    	Log.i(TAG,"Requesting URL : " + url);
    	consumer.sign(request);
    	HttpResponse response = httpclient.execute(request);
    	Log.i(TAG,"Statusline : " + response.getStatusLine());
    	InputStream data = response.getEntity().getContent();
    	BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(data));
        String responeLine;
        StringBuilder responseBuilder = new StringBuilder();
        while ((responeLine = bufferedReader.readLine()) != null) {
        	responseBuilder.append(responeLine);
        }
        Log.i(TAG,"Response : " + responseBuilder.toString());
        return responseBuilder.toString();
	}

The OAuthConsumer can be re-created at any time using the following code. Keep in mind that Signpost objects (consumer / provider) are very lightweight, and there is little to no overhead involved in re-creating them. The code below fetches our access token and token secret from the preferences (that were stored in the RetrieveAccessTokenTask execution), and re-creates our OAuthConsumer that we’ll use to sign our requests.

	private OAuthConsumer getConsumer(SharedPreferences prefs) {
		String token = prefs.getString(OAuth.OAUTH_TOKEN, "");
		String secret = prefs.getString(OAuth.OAUTH_TOKEN_SECRET, "");
		OAuthConsumer consumer = new CommonsHttpOAuthConsumer(Constants.CONSUMER_KEY, Constants.CONSUMER_SECRET);
		consumer.setTokenWithSecret(token, secret);
		return consumer;
	}

Source code for Implementing the OAuth flow in Android has been uploaded to Github.

References :


Viewing all articles
Browse latest Browse all 10

Trending Articles