Enterprise Integration Zone is brought to you in partnership with:

Java Geek and managing director@comSysto GmbH in Munich ... Spring over JavaEE, Apache Wicket over JSF, Gradle over Maven, Lean over Waterfall, exploring JavaFX, Highcharts, Android, AgileUX, Lean Startup. Daniel is a DZone MVB and is not an employee of DZone and has posted 41 posts at DZone. You can read more from them at their website. View Full User Profile

Integrating Google Calendar into a Wicket Application

05.14.2013
| 3991 views |
  • submit to reddit

I recently integrated Google Calendar into a Wicket application I use at home. As some requirements are atypical I want to describe them before going into too much detail:

  • As I use the application only by myself I want to allow anonymous access to my application. If multiple users access your system you have to implement “proper” authentication in your application though.
  • For the sake of simplicity I do not care about encrypting keys stored on my disk. However, if you use the code from this post in a real production system with multiple users please do yourself a favor and ensure any keys are stored as secure as possible.

Technical options

If you want to use a Google API in your application there is a chance that they already provide a client library, which is actually the case for Google Calendar. I decided to go another route for two reasons:

  1. As this is yet another REST API, there is no need for a special library and a new API. If I use Spring already I can achieve the same result by simply using Spring’s RestTemplate.
  2. I just wanted to create, update and delete all-day events with an event title. There is no need for a full blown API that allows all options such as support for recurring events, multiple participants or reminders.

Prior to accessing a Google API an application needs authorization from users to access their data which is handled by OAuth 2. As the application already uses Spring for various aspects, Spring Security OAuth 2 was the obvious choice for me as it integrates nicely into the rest of the application.

However, I faced two challenges: First, I needed to integrate Wicket and Spring Security OAuth 2 and second, Spring Security OAuth 2 needed some tweaks to work together with my setup.

Apart from Spring Security OAuth 2 I use the following libraries/frameworks:

  • Wicket as web framework
  • Spring as dependency injection container
  • Jackson for storing tokens and serializing and deserializing messages to the Google Calendar API
  • Gradle as build tool
  • Jetty as container

I have created a simple demo application which allows you to create an all-day event on any of your calendars. It is available on Github.

OAuth 2 in less than 100 words

There is plenty of information available on the web about OAuth 2, so I just want to describe briefly what OAuth 2 does:

Using OAuth 2 users can grant your application access to their data available through third-party services without providing you the credentials of the accounts you want to access. It involves three parties: Your users, your application (OAuth 2 client) and the authorization provider (OAuth 2 server). Apart from being users of your application they are also known to the authorization provider. When users access your application (step 1), it requests permission to e.g. access their data from a third party service (step 2). The authorization provider redirects to a page where users can grant your application access (step 3).

OAuth2 authorization scenario

OAuth2 authorization scenario (Icons courtesy of Adam Whitcroft)

Two great resources for in-depth information about OAuth 2 are Google’s OAuth 2 documentation and RFC 6749.

OAuth 2 for Web applications

Google’s OAuth 2 documentation describes different scenarios which can be used. I assume that you want to use the Webserver scenario, and specifically that you want to use “online access” (more on that later). In the Webserver scenario the user is redirected to Google’s authorization server before the first call of the application to the Google Calendar API. After the user has granted access, the authorization server redirects the user to your application along with an access token and the application may access the Google Calendar API with this access token.

Getting started

Before we dive into the code, we have to register the application with the OAuth provider. For Google, this is done via the API console. The process is pretty straightforward:

1. Create a new project and provide a descriptive name:

Create a new Google API project

2. Create a new client id. The client id identifies your application against the OAuth provider. Be sure to provide a custom redirect URL in the second screen.

Create a new client id for a Google API project (part 1)

Create a new client id for a Google API project (part 2)

3. After you have clicked “Create” the following overview is presented:

Google API project has been created

The most relevant pieces for your application are the client id and the client secret. As I have already described, the client id uniquely identifies your application with the OAuth 2 provider. The client secret authenticates your application with the OAuth 2 provider. If you want to think of traditional username/password based authentication, the client id loosely corresponds to the username of your application and the client secret corresponds to the password of your application with the OAuth 2 provider. Apparently, these two properties should not be shared.

Copy the values for “client id” into the property “google.calendar.client.id” and “client secret” into the property “google.calendar.client.secret” in the file application.properties if you follow along with the demo application.

4. Next, request access to the Google Calendar API for your application in the “Services” menu

Activate access to Google calendar API

Google Calendar Access using Spring OAuth 2

All accesses of the demo application to the Google Calendar API are encapsulated in the class GoogleCalendarRepositoryImpl. It uses an extension of the Spring standard interface RestOperations called OAuth2RestOperations which can handle OAuth 2 authorization in addition. Similar to the standard implementation of RestOperations, RestTemplate, Spring Security OAuth 2 provides a OAuth2RestTemplate. In the demo application, the OAuth2RestTemplate is configured in com/github/gcaldemo/calendar/spring-context.xml. First, let’s have a look at the „oauth:resource“ element there:

<oauth:resource id="google"
                type="authorization_code"
                client-id="${google.calendar.client.id}"
                client-secret="${google.calendar.client.secret}"
                access-token-uri="https://accounts.google.com/o/oauth2/token"
                user-authorization-uri="https://accounts.google.com/o/oauth2/auth"
                scope="https://www.googleapis.com/auth/calendar"
                client-authentication-scheme="form"
/>

This definition describes the resources we want to access. Apart from the client id and the client secret we have already discussed it also contains the URL to which users will be redirected when the application needs their approval to access their data (user-authorization-uri). The “scope” attribute defines the privileges the application wants to acquire. In this case we want read and write access to calendar data as indicated by the URL. The proper URL can be found in the Google Calendar API documentation.

Whenever the application tries to call the Google Calendar API, Spring OAuth 2 will check if the application has a valid access token. This access token is issued by the OAuth 2 provider and provided to the application after the user has granted access to its data. However, the access token is only valid for a limited time period which varies across OAuth providers. Google’s access tokens are currently valid for one hour. After that time period the access token is invalid and users have to grant the application access to their data again.

Fortunately, there are multiple means to prevent nagging users continuously:

  • The OAuth 2 RFC specifies a so called refresh token. The refresh token is sent by the OAuth 2 provider along with the first access token. It can be used to obtain a new access token upon expiration of the old one without user intervention. The validity period of the refresh token may be limited and depends on the OAuth provider. Google’s refresh token is valid until the application explicitly prompts the user explicitly for authorization. Note that Google sends the refresh token only in the „offline“ scenario. Offline means basically that an application can act on behalf of users without the user needing to be present (think batch processes). For more information on the refresh token please refer to the section on refresh tokens in Google’s OAuth 2 documentation.
  • In the online scenario (user is present when your application accesses the Google Calendar API), Google does not send a refresh token but rather stores a cookie on the client’s browser. This cookie will be used instead of a refresh token to prevent repeated explicit approval from the user. As I have hinted earlier, this scenario applies to the demo application.

The demo application provides a simple JSON based token store which is sufficient for a single user. It is implemented in JsonClientTokenServices, which performs the following tasks:

  • It stores the access token in a JSON file
  • It adjusts the expiry_in value: The expiry that is sent from the OAuth 2 provider is denoted in seconds from the point in time when the OAuth 2 provider has issued the access token. So, if the access token is valid for one hour, the initial value will be 3600 (60 seconds per minute * 60 minutes per hour). However, this is obviously not suitable for persistent storage. Therefore, the token store will adjust the expiry accordingly when loading an access token.

The token store is configured along with the access token provider:

<bean id="accessTokenProviderChain" class="org.springframework.security.oauth2.client.token.AccessTokenProviderChain">
<!-- Redefinition of the default access token providers  -->
  <constructor-arg index="0">
    <list>
      <bean class="org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider"/>
      <bean class="org.springframework.security.oauth2.client.token.grant.implicit.ImplicitAccessTokenProvider"/>
      <bean class="org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider"/>
      <bean class="org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider"/>
    </list>
  </constructor-arg>
  <property name="clientTokenServices">
    <bean class="com.github.gcaldemo.calendar.repository.impl.token.JsonClientTokenServices"/>
  </property>
</bean>

After defining both the resource to access and the access token provider, the OAuth2RestTemplate can be configured:

<oauth:rest-template id="googleCalendarRestTemplate"
                     resource="google"
                     access-token-provider="accessTokenProviderChain"/>

Integrating Spring OAuth 2 into Wicket

If the application has a valid access token, the OAuth2RestTemplate performs the API call, otherwise a UserRedirectRequiredException is thrown. Typically, the OAuth2ClientContextFilter, which is part of the Spring security chain, should catch this exception and redirect the user to the user-authorization-uri specified earlier. However, by default Wicket catches all exceptions that occur in the Web application and just dumps them in development mode or provides an error page in production mode. Hence, the UserRedirectRequiredException will never reach the Spring security filter chain. Therefore, we have to tweak exception handling by implementing a custom IExceptionMapper:

public class OAuth2ExceptionMapper implements IExceptionMapper {
  private final IExceptionMapper delegateExceptionMapper;
 
  public OAuth2ExceptionMapper(IExceptionMapper delegateExceptionMapper) {
    this.delegateExceptionMapper = delegateExceptionMapper;
  }
 
  @Override
  public IRequestHandler map(Exception e) {
    Throwable rootCause = getRootCause(e);
    if (rootCause instanceof UserRedirectRequiredException) {
      //see DefaultExceptionMapper
      Response response = RequestCycle.get().getResponse();
      if (response instanceof WebResponse) {
        // we don't want to cache an exceptional reply in the browser
        ((WebResponse)response).disableCaching();
      }
      throw ((UserRedirectRequiredException) rootCause);
    } else {
      return delegateExceptionMapper.map(e);
    }
  }
 
  private Throwable getRootCause(Throwable ex) {
    if (ex == null) {
      return null;
    }
    if (ex.getCause() == null) {
      return ex;
    }
    return getRootCause(ex.getCause());
  }
}

The custom exception mapper has to be created in the application by the exception mapper provider:

public class CalendarDemoApplication extends WebApplication {
  private IProvider<IExceptionMapper> exceptionMapperProvider;
 
  @Override
  protected void init() {
    super.init();
    this.exceptionMapperProvider = new OAuth2ExceptionMapperProvider();
    //details left out - see original class on Github
  }
 
  @Override
  public IProvider<IExceptionMapper> getExceptionMapperProvider() {
    return exceptionMapperProvider;
  }
 
  /**
   * Custom Exception Mapper provider that integrates the OAuth2ExceptionMapper into the application.
   */
  private static class OAuth2ExceptionMapperProvider implements IProvider<IExceptionMapper> {
    @Override
    public IExceptionMapper get() {
      return new OAuth2ExceptionMapper(new DefaultExceptionMapper());
    }
  }
}

Now UserRedirectRequiredException will not be handled by Wicket but propagated further up the call stack which allows the OAuth2ClientContextFilter to handle the exception properly. We are almost done but one last piece is still missing.

System-internal Authentication

As I have written in the introduction, I do not want to authenticate against my own application as I am the only user. However, Spring Security OAuth 2 expects user credentials within the application prior to authenticating the application against an OAuth 2 server. If we try to perform a Google Calendar API call as anonymous user we get the following trace:

org.springframework.security.authentication.InsufficientAuthenticationException: Authentication is required to obtain an access token (anonymous not allowed)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:88)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:217)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:169)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:90)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:479)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:124)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:446)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:214)
at com.github.gcaldemo.calendar.repository.impl.GoogleCalendarRepositoryImpl.loadCalendars(GoogleCalendarRepositoryImpl.java:45)
[...]

As the exception message tells, anonymous users are not allowed to perform OAuth 2 operations. Therefore, we have to grant access to the application only to authenticated users. This is done by configuring an interceptor in the http security configuration:

<security:intercept-url pattern="/**" access="ROLE_USER" />

Next, we have to trick Spring Security OAuth 2 by implementing a custom authentication processing filter which publishes a system user with proper privileges to the SecurityContext:

//some details omitted - see original class on Github
public class SystemAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
  @Override
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
    // Populate an internal system user in the security context with proper access privileges. These is typically not
    // necessary for multi user systems in production as users typically have to authenticate against your
    // application before using it.
    Authentication authentication = new TestingAuthenticationToken("internal_system_user", "internal_null_credentials", "ROLE_USER");
    authentication.setAuthenticated(true);
    return getAuthenticationManager().authenticate(authentication);
  }
 
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
    throws IOException, ServletException {
 
    if (SecurityContextHolder.getContext().getAuthentication() == null) {
      SecurityContextHolder.getContext().setAuthentication(attemptAuthentication((HttpServletRequest) req, (HttpServletResponse) res));
    }
    chain.doFilter(req, res);
  }
}

Now, Spring Security OAuth 2 can perform authorization requests properly and we are redirected before accessing the Google Calendar API for the first time:

Demo application requests access permission from Google

After the user has granted the application access to its data we can create an event. Note that the calendar drop down is already pre-filled with all Google calendars the user has at least write access to:

Creating a new Google calendar event

New Google calendar event has been created

Summary

Although a client library for the Google Calendar API is available it is sometimes feasible to use libraries and technologies that are already used in a project. With a few tweaks I was able to use the Google Calendar API in a Wicket application using Spring Security OAuth 2. The example application on Github demonstrates the integration but beware of the limitation that was mentioned above: This setup is primarily suited for single-user applications. If you want to reuse the sample code in a production environment you should use a ClientTokenServices implementation backed by a database and use a real implementation of AbstractAuthenticationProcessingFilter such as UsernamePasswordAuthenticationFilter.

I hope that this post described in enough detail how to integrate the Google Calendar API into a Wicket application. Otherwise, feel free to ask questions in the comments section.



Published at DZone with permission of Daniel Bartl, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)