Sunday, February 7, 2010

Add OAuth to your web application with Apache Camel

OAuth is an open protocol to allow secure API authorization from desktop and web applications. Google, for example, already supports OAuth for authorizing 3rd-party applications to access Google services on behalf of a user.

Recently, I added the gauth component to Apache Camel. You can use it to implement OAuth consumer functionality for any web application with only a few lines of code. gauth endpoints take care of exchanging authorization and access tokens between a web application and an OAuth service provider. At the moment, the gauth component can be used to interact with Google's OAuth services, later versions will support other OAuth providers as well.

From a user's perspective, an example OAuth scenario might look as follows:
  • The user logs into a web application that uses the Google Calendar API, for example.
  • To authorize access, the user is redirected to a Google Accounts authorization page where access for the requesting web application can be granted or denied.
  • After granting access the user is redirected back to the web application and the web application can now access the user's calendar data.
  • The user can revoke access at any time within Google Accounts.
To implement that scenario with Apache Camel, two routes are needed. The first route obtains an unauthorized request token from Google and then redirects the user to the Google Accounts authorization page:

String encodedCallback = URLEncoder.encode(
"https://example.org/handler", "UTF-8");
String encodedScope = URLEncoder.encode(
"http://www.google.com/calendar/feeds/", "UTF-8");

from("jetty:http://0.0.0.0:8080/authorize")
.to("gauth://authorize"
+ "?callback=" + encodedCallback
+ "&scope=" + encodedScope);

In this example, the authorization request is triggered by the user by sending a GET request to http://example.org/authorize (e.g. by clicking a link in the browser). The gauth://authorize endpoint then obtains an unauthorized request token from Google. The scope parameter in the endpoint URI defines which Google service the web application wants to access. After having obtained the token, the endpoint generates a redirect response (302) which redirects the user to the Google Accounts authorization page. After granting access, the user is redirected back to the web application (callback parameter). The callback now contains an authorized request token that must finally be upgraded to an access token. Handling the callback and upgrading to an access token is done in the second route.

from("jetty:https://example.org/handler")
.to("gauth://upgrade")
.to(new StoreTokenProcessor())

The jetty endpoint receives the callback from Google. The gauth://upgrade endpoint takes the authorized request token from the callback and upgrades it to an access token. The route finally stores the long-lived access token for the current user. The next time the user logs into the web application, the access token is already available and the application can continue to access the user's Google Calendar data without needing further user interaction. The user can invalidate the access token at any time within Google Accounts.

Only these two routes are needed to integrate with Google's OAuth provider services. The routes can perfectly co-exist with any other web application framework. Whereas the web framework provides the basis for web application-specific functionality, the OAuth service provider integration is done with Apache Camel. This approach allows for a clean separation of integration logic from application or domain logic.

For handling OAuth requests, web applications can also use other components than Camel's jetty component, such as the servlet component. For adding OAuth to Google App Engine applications, the jetty component needs to be replaced with Camel's ghttp component. Here's an example:

String encodedCallback = URLEncoder.encode(
"https://camelcloud.appspot.com/handler", "UTF-8");
String encodedScope = URLEncoder.encode(
"http://www.google.com/calendar/feeds/", "UTF-8");

from("ghttp:///authorize")
.to("gauth://authorize"
+ "?callback=" + encodedCallback
+ "&scope=" + encodedScope);

from("ghttp:///handler")
.to("gauth://upgrade")
.to(new StoreTokenProcessor())

The following figure gives an overview how the OAuth sequence of interactions relate to the gauth://authorize and gauth://upgrade endpoints.



Accessing a Google service with an access token (step 9) is application-specific and not covered by the gauth component. To get access to a user's Google Calendar data with an access token, one could use the GData client library. The gauth component documentation contains an example.

The gauth component is the first step towards a broader support of security standards such as OAuth and OpenID in Apache Camel. I'm currently thinking of the following extensions
  • A Camel OpenID component
  • A Camel OpenID/OAuth hybrid component
  • Support OAuth providers other than Google
The gauth component is currently part of the Camel 2.3 development snapshot (sources).

4 comments:

  1. Martin,

    Does your code handle the discovery process somehow as described in:
    http://code.google.com/googleapps/marketplace/tutorial.html#Integrate-OpenID

    Also, would it be too much to ask for you to give an article like "How to Write Your First Marketplace App using Camel?" Thanks!

    ReplyDelete
  2. Shaun,

    the Camel components for Google App Engine currently don't support OpenID and hence the OpenID discovery process. I'm planning to add OpenID support in one of the upcoming Camel versions. Once implemented I'm happy to write an article about developing Marketplace Apps with Camel.

    ReplyDelete
  3. Is this OAuth 2 or OAuth 1?

    ReplyDelete