Skip to main content

GWT and Open-ID using Spring Security

In this post I'll combine the GWT and Spring Security integration from http://technowobble.blogspot.com/2010/05/gwt-and-spring-security.html and the Open-ID using Spring Security from http://technowobble.blogspot.com/2010/06/using-spring-securitys-openid.html. I'm assuming you've read them before reading further... :)

I was also inspired by http://www.sociallipstick.com/?p=86 and http://code.google.com/p/dyuproject/wiki/OpenidLoginWithoutLeavingPage to get this working with a pop-up as my sample application is based on GWT - hence, I don't want to direct the user to another page and loose the application state etc.

I'm also showing how to exchange Open-ID attributes with e.g. Google. As with the previous blogposts, the sample application is runnable on Google App Engine.

With no further ado, this is basically what is needed to add Open-ID support to my previous sample application:

From my second post, add Openid4javaFetcher, MyHttpCacheProvider and OpenIdUserDetailsServiceImpl-classes, update the pom.xml with the necessary Open-ID dependencies and add the customized configuration of the OpenIDAuthenticationFilter into applicationContext-security.xml.

Now that all necessary Open-ID stuff is in place, let's start adding the functionality to the application from my first post:

First of all, let's add a "Sign in with Google" button to the LoginDialogBox:

 @UiHandler("googleLoginButton")
 void googleLogin(ClickEvent e) {
  Window.open("/j_spring_openid_security_check?openid_identifier=https://www.google.com/accounts/o8/id", "openid_popup", 

"width=450,height=500,location=1,status=1,resizable=yes");
 }

It will open up a pop-up window posting to the OpenIDAuthenticationFilter, which will take care of the rest of the Open-ID functionality. As discussed in the links mentioned above, I need a callback function that can be called once the Open-ID-request is return, i.e. from the pop-up window to the GWT application window. I therefor made the following changes:

 // general callback for any type of authentication scheme
 RequestCallback callback = new RequestCallback() {
     public void onError(Request request, Throwable exception) {
      showError();
         Log.error(exception.getMessage());
     }
     
     public void onResponseReceived(Request request, Response response) {
         if (response.getStatusCode() == Response.SC_OK) {
          // notify all interested components
          fireEvent(new LoginEvent(true));
          
          // issue the command that triggered the dialog
          if (cmd != null) {
           cmd.execute();
          }
          
          hide();
          
          Log.debug("[success (" + response.getStatusCode() + "," + response.getStatusText() + ")]");
         } else {
          showError();
          Log.error(response.getStatusCode() + "," + response.getStatusText());
         }
     }
 };

 // make the callback function available from JavaScript
 private native void exportMethods(LoginDialog instance) /*-{
  $wnd.handleOpenIDResponse = function(statusCode, statusText) {
   return instance.@com.myappenginecookbook.gwt.client.LoginDialog::onAuthentication(ILjava/lang/String;)(statusCode, 

statusText);
  }
 }-*/;
 
 public void onAuthentication(final int statusCode, final String statusText) {
  // call callback with the needed parameters
  callback.onResponseReceived(null, new Response() {
         @Override
         public String getHeader(String header) {
          return null;
         }

         @Override
         public Header[] getHeaders() {
           return null;
         }

         @Override
         public String getHeadersAsString() {
           return null;
         }

         @Override
         public int getStatusCode() {
           return statusCode;
         }

         @Override
         public String getStatusText() {
           return statusText;
         }

         @Override
         public String getText() {
           return "";
         }
      });
 }

Note that I've broken out the anonymous RequestCallback so that it can be used with both ways of authenticating.

What's left now is to make sure the callback function will be called, which is the responsiblity of the OpenIdAuthenticationFailureHandler and OpenIdAuthenticationSuccessHandler-classes, which will be called by Spring Security at the end of the Open-ID request:

package com.myappenginecookbook.security;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

public class OpenIdAuthenticationSuccessHandler implements
  AuthenticationSuccessHandler {

 @Override
 public void onAuthenticationSuccess(HttpServletRequest request,
   HttpServletResponse response, Authentication authentication)
   throws IOException, ServletException {
  
  response.setContentType("text/html");
  PrintWriter out = response.getWriter();
  
  String html = 
         "<html>" +
      "<head>" +
      "</head>" +
      "<body onload=\"window.opener.handleOpenIDResponse("+HttpServletResponse.SC_OK+",'Authentication accepted');window.close

();\">" +
      "</body>" +
      "</html>";
        
        out.print(html);          
 }
}

The OpenIdAuthenticationSuccessHandler will render a simple html-fragment which utilizes JavaScript to call the exported GWT-method to signal the sucess/failure of the request. The OpenIdAuthenticationFailureHandler is basically identical, but will call the callback with the SC_UNAUTHORIZED http-code instead.

To finalize the upgraded sample app I searched the forums on how to get hold of the exchanged attributes and came up with this thread, and created a CustomOpenIDAuthenticationProvider-class to deal with it:

package com.myappenginecookbook.security;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.openid.OpenIDAttribute;
import org.springframework.security.openid.OpenIDAuthenticationProvider;
import org.springframework.security.openid.OpenIDAuthenticationToken;

public class CustomOpenIDAuthenticationProvider extends
  OpenIDAuthenticationProvider {

 @Override
 protected Authentication createSuccessfulAuthentication(
   UserDetails userDetails, OpenIDAuthenticationToken auth) {
  
  if (userDetails instanceof CustomUser) {
   CustomUser user = (CustomUser) userDetails;
   for (final OpenIDAttribute attribute : auth.getAttributes()) {
        if ("email".equals(attribute.getName())) {
          String email = attribute.getValues().get(0);
          user.setEmail(email);
        }
      }
  }

  return super.createSuccessfulAuthentication(userDetails, auth);
 }
}

Hope this helps in getting your own applications Open-ID aware! Sourcecode can be found here.

Comments

  1. Juan Carlos GonzálezNovember 12, 2010 at 5:51 PM

    Hi Mattias.

    I'm trying to test this approach and I can run the project, but when clicking the Sign in with Google button I get the following stack trace:

    java.lang.NoClassDefFoundError: org/apache/commons/codec/binary/Base64
    at org.openid4java.association.DiffieHellmanSession.publicKeyToString(DiffieHellmanSession.java:306)
    at org.openid4java.association.DiffieHellmanSession.getPublicKey(DiffieHellmanSession.java:206)
    at org.openid4java.message.AssociationRequest.....

    I've removed the rest because of lenght limit.

    In your previous post regarding OpenId, Spring and GAE, you created a pom.xml file witrh several dependencies to commons library. However, this project do not contain the same commons references.

    Please, could you help me with this?.

    K.R.

    Juan Carlos

    ReplyDelete
  2. Juan Carlos GonzálezNovember 16, 2010 at 10:31 AM

    Hi Mattias,

    I've added the following dependency (got it from openid4java pom file) to the pom.xml file and now it works properly. I've deployed to app engine and works as expected.


    commons-codec
    commons-codec
    1.3


    K.R.

    Juan Carlos

    ReplyDelete
  3. Thanks for sharing the solution!

    I was just about to tell you that I can't give you free consultancy hours and support all things I write on my blog... :)

    ReplyDelete
  4. Thanks for the posts. I notice you have several related posts regarding GWT, OpenID & Spring Security. I'm confused as to which is the most current. Can you clarify which is most recent? Also do you happen to have a download of all sample source code? Thanks!

    ReplyDelete
  5. Hi, this is the latest post and combines all three of GWT, Spring Security and Open-ID. The previous posts only used either only GWT/Spring Security or Spring Security/Open-ID.

    All source code should be linked in the posts - see the link at the end of this post, for example.

    Thanks for reading!

    ReplyDelete

Post a Comment

Popular posts from this blog

Getting filters to play nicely with Spring

After having some struggle with getting filters to integrate well with my Spring context, I decided to write down a small tutorial for it...

To begin with, filters are defined in web.xml and are therefore not automatically part of the Spring application context, which is normally set up using the DispatcherServlet. A good way to abstract away all this is to use the DelegatingFilterProxy, which is a Spring utility class that acts as a filter, but will delegate the doFilter-method to a Spring-managed bean - normally a class extending GenericFilterBean.

As a filter is defined in web.xml and looks for the bean defined in the Spring context in applicationContext.xml, it's not really the set up that I want. Filters are part of a web application and should have access to what's usually defined in webmvc-config.xml, as part of the DispatcherServlet set up. To be able to use, e.g. request scoped beans in applicationContext.xml, you need to defined them as proxied objects in order to have…

GWT and Spring Security

Update! - Based on the post below, and my other post regarding Spring Security and OpenID, I have added Open-ID support to the sample application below. For those interested, here's the write-up of changes.



I've spent quite some time digging into ways of integrating GWT and Spring Security. It all started by reading the following post in the GWT Forum - Best practices/ideas for GWT with Spring Security (or equivalent), and then checking out this blog - GWT and Spring Security. To make matters worse, I started reading Security for GWT Applications and specifically about the "Cross-Site Request Forging"-attacks.

Now, what could I do about it?

Well, starting by setting up my own project (Maven-based) with all updated dependencies (GWT 2.0.3 etc) and started reading the Spring Security Reference Documentation (puh!).

Instead of See Wah Cheng's approach of implementing a custom authentication service, I decided to rely on standard namespace configuration (with some ex…

Using SmartGWT with Jersey RESTful backend on Spring Roo

I decided to give SmartGWT a run, and specifically the RestDataSource functionality using Jersey. To make things easier, I'm using Spring Roo to set up my project and scaffold much of the boilerplate code, configurations etc.

My approach is to have my domain objects exposed as RESTful web services and create a matching SmartGWT DataSource in order to show them in a GUI.

Let's get started, shall we?

First of all, you'll need Spring Roo (unless you just download the project source code and want to run it directly). I'm using the following Roo script to start things off:

project --topLevelPackage com.technowobble persistence setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY entity --class ~.domain.Message field string --fieldName value controller class --class ~.ws.MessageResource --preferredMapping rest dependency add --groupId com.sun.jersey --artifactId jersey-server --version 1.3 dependency add --groupId com.sun.jersey.contribs --artifactId jersey-spring --…