Sunday, February 7, 2010

Accessing a security-enabled Google App Engine service with Apache Camel

In a previous post I've described the low-level details for programmatic login to a Google App Engine service from a Java client. Things are getting much easier when using Apache Camel. The recently committed glogin component makes it trivial to login to a remotely deployed Google App Engine service as well as to a local development server. In the following example, an application-specific authorization cookie is obtained with the glogin component. It authorizes a client application to access http://camelcloud.appspot.com on Google App Engine.

import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import static org.apache.camel.component.gae.login.GLoginBinding.*;

...

ProducerTemplate template = ...

Exchange result = template.request(
"glogin://camelcloud.appspot.com"
+ "?userName=replaceme@gmail.com"
+ "&password=replaceme", null);
String cookie = result.getOut().getHeader(
GLOGIN_COOKIE, String.class));

Please note that the password is only sent to the Google Accounts API for authentication. It is never sent to Google App Engine or included into any URL. The obtained authorization cookie is valid for 24 hours and needs to be sent with subsequent requests to the GAE application. If inclusion of user credentials in an endpoint URI is not an option, username and password can also be dynamically set (per request) using Camel message headers:

import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import static org.apache.camel.component.gae.login.GLoginBinding.*;

...

ProducerTemplate template = ...

Exchange result = template.request(
"glogin://camelcloud.appspot.com", new Processor() {
public void process(Exchange exchange) {
exchange.getIn().setHeader(
GLOGIN_USER_NAME, "replaceme@gmail.com");
exchange.getIn().setHeader(
GLOGIN_PASSWORD, "replaceme");
}
});
String cookie = result.getOut().getHeader(
GLOGIN_COOKIE, String.class));

To login to a local development server, the devMode parameter in the endpoint URI must be set to true.

import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import static org.apache.camel.component.gae.login.GLoginBinding.*;

...

ProducerTemplate template = ...

Exchange result = template.request(
"glogin://localhost:8888"
+ "?userName=test@example.org"
+ "&devMode=true", null);
String cookie = result.getOut().getHeader(
GLOGIN_COOKIE, String.class));
The glogin component is part of the Camel Components for Google App Engine.

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).