Tuesday, June 22, 2010

Using Spring Security's OpenID implementation (openid4java) on Google App Engine

The goal with this exercise is to have a running example of an OpenID login on a simple Spring application, using Google as the OpenID Provider. Note that the application will be running on Google App Engine and that Spring Roo is only used for simplicity of creating the project files. Any Spring-based application could use the same implementation.

First of all, create a simple project using Spring Roo (or any equivalent framework), including the default security setup:

project --topLevelPackage com.technowobble
persistence setup --provider DATANUCLEUS --database GOOGLE_APP_ENGINE
entity --class ~.domain.MyEntity
field string --fieldName name
controller all --package com.technowobble.controller
security setup

This setup only provides us with a form-login, which is not what we wanted. So what about OpenID?

Well, if it wasn't for Google App Engine, I would happily have added an <openid-login>-tag to applicationContext-security.xml, but things are never that easy, are they? What happens is that the OpenIDAuthenticationFilter that would have been injected relys on openid4java, which in turn uses HttpClient4 - known not to work on GAE. There is, however, a workaround that can be made which involves using the latest snapshot of openid4java (0.9.6-SNAPSHOT) instead of the stable release (0.9.5) and using the HttpFetcherFactory-functionality to inject a HttpFetcher that
actually works with GAE.

Let's add the dependencies to the pom.xml:

dependency add --groupId org.springframework.security --artifactId spring-security-openid --version  ${spring.version}
dependency add --groupId org.openid4java --artifactId openid4java-consumer --version 0.9.6-SNAPSHOT

Make sure to exlude the dependency for the stable release by adding the following to the spring-security-openid artifact in your pom.xml:

<exclusions>
 <exclusion>
 <groupId>org.openid4java</groupId>
 <artifactId>openid4java</artifactId>
 </exclusion>
</exclusions>

Note - you need to build openid4java 0.9.6-SNAPSHOT yourself and install it to your repository, unless you can't find a public repository that has it.

Next thing is to configure the OpenIDAuthenticationFilter manually in applicationContext-security.xml, in order to get to the point where the HttpFetcherFactory can be injected:

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-

3.0.xsd">

 <!-- HTTP security configurations -->
    <http auto-config="true" use-expressions="true">
     <form-login login-processing-url="/static/j_spring_security_check" login-page="/login" authentication-failure-url="/login?

login_error=t"/>
        <logout logout-url="/static/j_spring_security_logout"/>
        
        <!-- Configure these elements to secure URIs in your application -->
        <intercept-url pattern="/myentitys/**" access="isAuthenticated()" />
 <intercept-url pattern="/choices/**" access="hasRole('ROLE_ADMIN')"/>        
        <intercept-url pattern="/member/**" access="isAuthenticated()" />
        <intercept-url pattern="/resources/**" access="permitAll" />
        <intercept-url pattern="/static/**" access="permitAll" />
        <intercept-url pattern="/**" access="permitAll" />
 
 <!-- Can't add the <openid-login>-tag, as it's default filter violates app engine's whitelist -->
 <custom-filter position="OPENID_FILTER" ref="myOpenIDAuthenticationFilter" /> 
    </http>
    
    <!-- Start configuration of OpenId-filter -->    
     <beans:bean id="myOpenIDAuthenticationFilter" class="org.springframework.security.openid.OpenIDAuthenticationFilter">
  <beans:property name="authenticationManager" ref="authenticationManager"/>
    <beans:property name="consumer" ref="myOpenID4JavaConsumer"></beans:property>
 </beans:bean>
 
 <beans:bean id="myOpenID4JavaConsumer" class="org.springframework.security.openid.OpenID4JavaConsumer">
  <beans:constructor-arg index="0" ref="myConsumerManager"></beans:constructor-arg>
  <beans:constructor-arg index="1">
   <beans:list value-type="org.springframework.security.openid.OpenIDAttribute">
    <beans:bean class="org.springframework.security.openid.OpenIDAttribute">
     <beans:constructor-arg index="0" value="email"/>
     <beans:constructor-arg index="1" value="http://axschema.org/contact/email"/>
     <beans:property name="required" value="true"/>
    </beans:bean>
   </beans:list>
  </beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myConsumerManager" class="org.openid4java.consumer.ConsumerManager">
  <beans:constructor-arg index="0" ref="myRealmVerifierFactory"></beans:constructor-arg>
  <beans:constructor-arg index="1" ref="myDiscovery"></beans:constructor-arg>
  <beans:constructor-arg index="2" ref="myHttpFetcherFactory"></beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myRealmVerifierFactory" class="org.openid4java.server.RealmVerifierFactory">
  <beans:constructor-arg index="0" ref="myYadisResolver"></beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myYadisResolver" class="org.openid4java.discovery.yadis.YadisResolver">
  <beans:constructor-arg index="0" ref="myHttpFetcherFactory"></beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myHttpFetcherFactory" class="org.openid4java.util.HttpFetcherFactory">
  <beans:constructor-arg index="0" ref="myProvider"></beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myProvider" class="com.technowobble.security.MyHttpCacheProvider"></beans:bean>
 
 <beans:bean id="myDiscovery" class="org.openid4java.discovery.Discovery">
  <beans:constructor-arg index="0" ref="myHtmlResolver"></beans:constructor-arg>
  <beans:constructor-arg index="1" ref="myYadisResolver"></beans:constructor-arg>
  <beans:constructor-arg index="2" ref="myXriResolver"></beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myHtmlResolver" class="org.openid4java.discovery.html.HtmlResolver">
  <beans:constructor-arg index="0" ref="myHttpFetcherFactory"></beans:constructor-arg>
 </beans:bean> 
 
 <beans:bean id="myXriResolver" class="org.openid4java.discovery.xri.XriDotNetProxyResolver">
  <beans:constructor-arg index="0" ref="myHttpFetcherFactory"></beans:constructor-arg>
 </beans:bean>
 <!-- End configuration of OpenId-filter --> 

 <!-- Configure Authentication mechanism -->
    <authentication-manager alias="authenticationManager">
     <!-- SHA-256 values can be produced using 'echo -n your_desired_password | sha256sum' (using normal *nix environments) -->
     <authentication-provider>
      <password-encoder hash="sha-256"/>
         <user-service>
             <user name="admin" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918" authorities="ROLE_ADMIN"/>
          <user name="user" password="04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb" 

authorities="ROLE_USER"/>
      </user-service>
     </authentication-provider>
 <authentication-provider ref="myOpenIDAuthenticationProvider" />
 </authentication-manager>
 
 <beans:bean id="myOpenIDAuthenticationProvider" class="org.springframework.security.openid.OpenIDAuthenticationProvider">
  <beans:property name="userDetailsService" ref="myOpenIdUserDetailsService"></beans:property>
 </beans:bean>
 
 <beans:bean id="myOpenIdUserDetailsService" class="com.technowobble.security.OpenIdUserDetailsServiceImpl"></beans:bean> 

</beans:beans>

There is only one class-reference that is interesting from a GAE perspective in the configuration above - MyHttpCacheProvider. This will inject an Openid4javaFetcher instance that is using a GAE-friendly URLFetchService (taken from the step2 project:

package com.technowobble.security;

import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
import org.openid4java.util.HttpFetcher;
import com.google.inject.Provider;
import com.google.step2.example.consumer.appengine.Openid4javaFetcher;

public class MyHttpCacheProvider implements Provider<HttpFetcher> {

 @Override
 public HttpFetcher get() {
  return new Openid4javaFetcher(URLFetchServiceFactory.getURLFetchService());
 } 
}

Another thing to note is the added intercept-url pattern for "myentitys/**" which secures access to the domain objects created earlier. (This could of course be changed to whatever that needs to be secured).

There's also a custom UserDetailsService that handles the creation of UserDetails-objects using the supplied OpenID information:

package com.technowobble.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * Implementation of {@link UserDetailsService} for OpenId
 */
public class OpenIdUserDetailsServiceImpl implements UserDetailsService {

 @Override
 public UserDetails loadUserByUsername(String username)
   throws UsernameNotFoundException, DataAccessException {
  return new User(username, "", true, true, true, true, getAuthorities());
 }
 
 /**
  * Utility method for creating a list of {@link GrantedAuthority} objects
  * @return
  */
 private Collection<GrantedAuthority> getAuthorities() {
  List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(2);
  authList.add(new GrantedAuthorityImpl("ROLE_USER"));
  
  return authList;
 }
}

The last thing to be done is to add an option to log in using OpenID, by adding the following form to webapp/WEB-INF/views/login.jspx:

<spring:url value='/static/j_spring_openid_security_check' var="openid_url"/>
<form name="o" action="${openid_url}" method="POST">    
 <div>
     <input type="hidden" id="openid_identifier" name='openid_identifier' value="https://www.google.com/accounts/o8/id"/>
 </div>
 <div class="submit">
     <script type="text/javascript">Spring.addDecoration(new Spring.ValidateAllDecoration({elementId:'proceed', 

event:'onclick'}));</script>
     <input id="proceed" type="submit" value="Sign in with Google"/>
 </div>         
</form>


Everything regarding security is now set up, but there are a few more things to do before we can deploy it using "mvn gae:deploy" (at least if using Spring Roo 1.1.0.M1).

First of all we need to downgrade the datanucleus-appengine artifact to version 1.0.4, as GAE throws an exception when using the default 1.0.7.final from Spring Roo. Secondly, to make the jspx-files compile, we need to add an empty dummy.jsp to webapp/WEB-INF/views (triggers the pre-compilation) and a <jsp:directive.page isELIgnored="false"/> directive to webapp/WEB-INF/layouts/default.jspx (takes care of jstl-versioning problems).

Make sure to update appengine-web.xml with your own application id/version and type "mvn gae:deploy". This command will take an unacceptable amount of time due to precompilation of all jspx-files (supposedly), but will eventually succeed, and you'll be asked for your app engine credentials. Feet up and relax!

A full source of the example project can be found here.

Please note that I haven't successfully managed to run this locally using "mvn gae:run", so that's one question I won't be able to answer. One option might be to switch to HIBERNATE/HYPERSONIC and use "mvn tomcat:run" instead, but I haven't tried it. Note that you have to comment out the constructor argument for the myHttpFetcherFactory-bean in your applicationContext-security.xml, because you're no longer in a GAE environment!

Monday, June 14, 2010

Adding a custom GWT module in a Roo-project

So I started looking into Roo the other day. Seems like a nice tool (once it matures a little more). Anyway - I wanted to create a custom module in addition to the two being generated when adding GWT-support to the project. Not as easy as I thought, mainly because I hadn't seen the Url Rewrite Filter before. I ended up with a 404, no matter what I did, and couldn't figure out why. Frustration!

After reading up on the url rewrite stuff, I was ready to give it another try. Let's create a module called Application, based on the standard sample application you'll get using the GWT wizard in Eclipse. First of all, create the Roo project using the following instructions:

project --topLevelPackage com.technowobble
persistence setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY
entity --class com.technowobble.domain.MyEntity
gwt setup

(The "gwt setup" setup command generates some code that will not compile unless there's a persistence setup and at least one entity.)

Next, create the Application.gwt.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.0.1//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.0.1/distro-source/core/src/gwt-module.dtd">
<module rename-to="application">
  <inherits name="com.google.gwt.user.User" />
  <inherits name="com.google.gwt.user.theme.standard.Standard" />

  <inherits name="com.technowobble.gwt.ApplicationCommon" />

  <source path="client" />
  <source path='shared'/>
  
  <entry-point
    class="com.technowobble.gwt.client.Application" />
</module>

We also need to add the actual Application-class (Application.java) and the service classes (GreetingService.java, GreetingServiceAsync.java, GreetingServiceImpl.java and FieldVerifier) from the Eclipse GWT sample application. While we're at it, put the Application.html and Application.css into the src/main/webapp folder.

Of course, the service servlet needs to be added to web.xml:

<servlet>
    <servlet-name>greetServlet</servlet-name>
    <servlet-class>com.technowobble.gwt.server.GreetingServiceImpl</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>greetServlet</servlet-name>
    <url-pattern>/application/greet</url-pattern>
  </servlet-mapping>

Before we can access the new module we need to add the following rules to urlrewrite.xml:

<rule enabled="true">
        <from casesensitive="false">/application/**</from>
        <to last="true" type="forward">/application/$1</to>
    </rule>
    <rule enabled="true">
        <from casesensitive="false">/Application.html</from>
        <to last="true" type="forward">/Application.html</to>
    </rule>    

The html file contains a reference to the stylesheet, which needs to be updated due to the Url Rewrite Filter - just change the href to href="static/Application.css".

That's it. Of course, an interesting exercise would be to combine this with the use of the DispatcherServlet and a Spring/GWT-integration controller like the GwtRpcController being used in my blog entry about Spring Security and GWT

You can find the full source here.

Sunday, June 6, 2010

How to integrate Facebook's JavaScript SDK with GWT

First of all - you need to have a Facebook account. Then you need to create an application on Facebook at http://www.facebook.com/developers/createapp.php, that will give you an App Id. This id is tightly connected to the url/site from which you will be using the JavaScript SDK and you'll be using it later.

For development, localhost addresses works fine - i.e. http://127.0.0.1:8888.

Next thing is to add the actual JavaScript library to you hostpage, which according to Facebook means adding this piece of JavaScript just before the body-endtag:

<!-- Facebook integration -->
<div id="fb-root"></div>
<script>
(function() {
var e = document.createElement('script'); e.async = true;
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
}());
</script>

It will dynamically add a script-tag into your html-file, which bypasses the "Same Origin Policy" all together. (No need for a xd_receiver.htm, as with previous versions of the API, that is).

Now it's time to initialize the Facebook API using native methods in GWT:

private native String initFacebookAPI()
/*-{
$wnd.FB.init({appId: '<YOUR_APP_ID_HERE>', status: true, cookie: true, xfbml: true});
$wnd.FB.Event.subscribe('auth.sessionChange', function(response) {
if (response.session) {
// A user has logged in, and a new cookie has been saved
$wnd.onLogin();
} else {
// The user has logged out, and the cookie has been cleared
$wnd.onLogout();
}          
});
}-*/;

$wnd.FB works because we dynamically included the Facebook .js-file in the previous step. So, anything you'd want to do when initializing the API can be done here, e.g. adding subscriptions to events (as above).

As can be seen, two different methods are being called when a sessionChange-event occurs (depending on what happened). These methods will be regular GWT-methods, but we need to export them into the DOM in order to call them from within native code:

private native void exportMethods(Application instance) /*-{
$wnd.onLogin = function() {
return instance.@com.technowobble.gwt.client.Application::onLogin()();
}
$wnd.onLogout = function() {
return instance.@com.technowobble.gwt.client.Application::onLogout()();
}
$wnd.onAPICall = function(callback, response, exception) {
return instance.@com.technowobble.gwt.client.Application::onAPICall

(Lcom/google/gwt/user/client/rpc/AsyncCallback;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)

(callback, response, exception);
}
}-*/;

There's also on more function being exported (which needs some explanation...) It serves as a callback function for a native method exposing the Facebook Graph API:

private native void callAPI(String path, AsyncCallback<JavaScriptObject> callback) /*-{
$wnd.FB.api(path, function(response) {
if (!response) {
alert('Error occured');
} else if (response.error) {
alert($wnd.dump(response));
// call callback with the actual error
$wnd.onAPICall(callback, null, response.error);
} else if (response.data) {
alert($wnd.dump(response));
// call callback with the actual json-array
$wnd.onAPICall(callback, response.data, null);
} else {
alert($wnd.dump(response));
// call callback with the actual json-object
$wnd.onAPICall(callback, response, null);
} 
});
}-*/;

What happens here is that any GWT-method now can make calls like this:

callAPI("/me", new AsyncCallback<JavaScriptObject>() {

@Override
public void onFailure(Throwable caught) {
Window.alert(caught.getMessage());
}

@Override
public void onSuccess(JavaScriptObject result) {
UserJso fbUser = (UserJso) result;
Window.alert(fbUser.getFullName());
}
});   

The onAPICall exported previously is the method binding the first call to the AsyncCallback:

public void onAPICall(AsyncCallback<JavaScriptObject> callback,
JavaScriptObject response, JavaScriptObject exception) {
if (response != null) {
callback.onSuccess(response);
} else {
ExceptionJso e = (ExceptionJso) exception;
callback.onFailure(new Exception(e.getType() + ": " + e.getMessage()));
}
}

Note that the AsyncCallback used for a specific Graph API call, e.g. "/me" is free to cast the result to an overlay type (the result should be a json-representation). The UserJso looks like this:

package com.technowobble.gwt.client.json;

import com.google.gwt.core.client.JavaScriptObject;

public class UserJso extends JavaScriptObject {
// Overlay types always have protected, zero-arg constructors
protected UserJso() { }

public final native String getFirstName() /*-{ return this.first_name; }-*/;
public final native String getLastName()  /*-{ return this.last_name;  }-*/;

public final String getFullName() {
return getFirstName() + " " + getLastName();
}
}

A full example project can be found here and should get you started on more elaborative usage.