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:
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:
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:
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:
Hope this helps in getting your own applications Open-ID aware! Sourcecode can be found here.
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.
Hi Mattias.
ReplyDeleteI'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
Hi Mattias,
ReplyDeleteI'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
Thanks for sharing the solution!
ReplyDeleteI was just about to tell you that I can't give you free consultancy hours and support all things I write on my blog... :)
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!
ReplyDeleteHi, 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.
ReplyDeleteAll source code should be linked in the posts - see the link at the end of this post, for example.
Thanks for reading!
Casino de joker123【Malaysia】⚡【WG98.VIP】⭐www.casinonewsdaily.com
ReplyDeleteCasino 카지노사이트 de joker123【WG98.VIP】⭐www.casinonewsdaily.com. casino 카지노사이트 de sbobet ทางเข้า joker123【WG98.VIP】⭐www.casinonewsdaily.com
Thank you for taking the time to share this
ReplyDelete