Motivation

OpenID is a service providing single-sign-on features. At our institute we are operating an OpenID provider that delegates authentication to our ActiveDirectory where all our students are registered. For a course planning system I was assessing Tapestrys integration possibilities with this OpenID provider and found out, that the tapestry-spring-security project already has much of the stuff needed for integration with our provider on board. This is a description of what you have to do in order to have OpenID authentication in your Tapestry apps.

Background

Spring Security already comes with everything needed for authenticating users against an OpenID provider (although this is somewhat incomplete regarding additional attributes that can be provided by the OpenID provider). But the tapestry-spring-security lacks the configuration needed to use this out of the box. In particular it lacks the filter configuration for the OpenIDAuthenticationProcessingFilter that processes incoming OpenID authentications. In a "normal" setup this is just another Filter that you configure in your web.xml. In a Tapestry setup though, Tapestry receives all incoming requests and processes them in the HttpServletRequestHandler pipeline. Fortunately this pipeline can be extended by contributing HttpServletRequestFilters. So all we need to do is wrap the OpenIDAuthenticationProcessingFilter and contribute it to the HttpServletRequestHandler pipeline. Such a wrapper is provided by the tapestry-spring-security project. With that in place we can process incoming OpenID authentication requests. Next, we have to look up the authenticated user in our local database where we store the roles assigned to a user. For this we have to implement a UserDetailsService which retrieves a user and his roles from some data store. The user object that is returned has to implement the UserDetails interface and the roles he holds have to implement the GrantedAuthority interface. If you don't want to grant access to some OpenID authenticated user, you have to do that in the UserDetailsService. The implementation I will show you grants accesss to EVERY OpenID authenticated user so don't use this in a "real" environment.

Requirements

tapestry-hibernate and tapestry-spring-security have to be in your pom.xml (or their jars and all of their dependencies have to be in your classpath). Configuring tapestry-hibernate is outside the scope of this article, please consult it's own documentation.

Implementation

Let's begin with the User and Role objects that implement the UserDetails and GrantedAuthority interfaces, respectively. These are used to store users and their roles in a local database using Hibernate. The User is a minimalistic implementation that only stores an id and a username.

Tying it together

Setting up the OpenID authentication is done in two steps. First we need to add an AuthenticationProvider that provides OpenID authentication and contribute this to the ProviderManager service which is defined by the tapestry-spring-security module. The OpenIDAuthenticationProvider comes directly from Spring security and just has to be set up correctly. Basically it is some kind of wrapper around the UserDetailsService. The following code builds the UserDetailsService and the OpenIDAuthenticationProvider and contributes it to the ProviderManager service:

Next we have to define ourselves a URL to which incoming OpenID authentication requests will be send and which the filter intercepts. Background: Every filter is intercepting requests for it's specific URL. If we just used the URL of an existing filter, our OpenID filter would intercept requests for this filter, thus making authentication fail.

And that's it. If you now secure your pages/methods with the @Secured annotation and provide a login page, you should be able to login with your OpenID. For the sake of completeness, here is the Login page and it's template: