Apr 21

For those of you who don’t know what OpenID is, get to know it now. This post is going to be about how to use OpenID authentication in your GWT applications.

First, a reminder of what OpenID is:

OpenID is a free and easy way to use a single digital identity across the Internet.

My point of view is that OpenID allows site developers not to worry about expensive authentication processes while giving its users the comfort of trusting one supplier of authentication to keep their password and other identification means safe from the bad guys out there. For example, by using OpenID your users who have signed up to Verisign’s PIP can use their keyring dongle to log in to your site, without writing a single line of code specific to keyrings at all.

Design considerations

While OpenID has great library support in most web development languages including Java, it seemed to me that GWT required a bit of tweaking in order to get it to work:

  • The HttpServletResponse object received by the GWT servlet RemoteServiceServlet does not allow redirections.
  • GWT’s concept is that there are no long-term sessions. The HttpServletRequest.getSession() method returns a local session object, and data cannot be stored there, so information regarding the OpenID authentication process needed to be stored somewhere else.

In addition, I wanted the resulting classes dealing with the OpenID authentication to be reusable, unit-tested, and decoupled from the method used to store the authentication process information. In order to achieve the decoupling, I’ve created a callback interface which the application holding the storage space implements. The reusability is achieved thanks to this decoupling, and by making it a standard HttpServlet (more on that later): the class only contains OpenID authentication logic, and no application logic. Unit-testing the class was trickier, since the class was a servlet and required an HTTP conversation to take place for it to be tested. However, that was achieved using the Cactus framework (that too required a few tricks.. But that story is for a different post!)

Implementation

At first, I’ve decided on the basic interface the GWT service should have:


public interface UserAuthentication extends RemoteService {
  LoginResult login(String openIdURL);
  UserDetails getCurrentUser();
  void logout();
}

After that, I’ve made the sequence diagram which covers what happens behind the scenes:

OpenID Servlet Sequence Diagram

The implementation code can be found here. There are a few things worth noting about the it:

  1. The login method does not return the user details. Instead, it either returns that the user was successfully logged in or that it was not, and attaches a URL. The client then redirects itself to that URL. Note this simple JSNI code for client-based redirection.
  2. The first URL the client receives (the one mentioned in the last point) is actually a URL still within the web application; the only difference is that it is not a GWT servlet. This is a big difference, as it allows implementing the doGet method and allows for real redirection, using the HttpServletResponse.sendRedirect method.
  3. Apparently, GWT supports having servlets of all kinds, including ones that are not GWT servlets. This is how the OpenID servlet is added: just as a normal tag: <servlet path=”/openid” class=”my.pkg.OpenIDServlet” />.
  4. The first cookie the client receives, the “unique ID” cookie, is what will identify the client when he gets back from the OpenID provider. This is saved to some shared record available to all web servers running the application, containing this unique ID and, if the client returns with a successful log in credentials from his OpenID provider, the client’s OpenID user URL as well.
  5. The callback implementation is supposed to save the information on some storage. In my application, I used Hibernate to store a small class containing a random UUID (the unique ID sent on the first cookie) and the OpenID user URL. I then used Hibernate again to create a join between this record and my user details record, which contained application specific data. That way, even the storage solution I chose was decoupled from my application.
  6. Instantiating the callback was a task handed to a dependency injection mechanism, as I didn’t want my application code to deal with that. The mechanism I chose to use was Google’s Guice.
  7. I chose to use OpenID4Java as the OpenID implementation. Afterwards, I noticed that Verisign had their own implementation, JOID, which keeps its own data store, ridding the need to do it yourself. I didn’t try it though, so I can’t say whether it works as a “drop-in” library to my implementation of the servlet, or whether its easier to use.

As always, I’m looking forward to your comments!

Update: after successive requests, I give in! You can find the (very simple) HttpCookies and DependencyInjection classes here as well!

Related Posts with Thumbnails

45 Responses to “Using OpenID within GWT”

  1. Severus Says:

    Thanks a lot for this post!!

  2. Aviad Says:

    You’re welcome Severus! Thanks for the comment!

  3. foo Says:

    hi aviad,

    thanks for sharing. good stuff.

    by the way, are you able to debug openid within gwt via the gwt hosted mode? the redirects are causing some problems for me.

  4. Aviad Says:

    Hi foo!

    Unfortunately, I haven’t been really able to “debug” openid – I send the redirect, and pray. :)

    More sersiouly, openid4java is open-source and therefore you can debug it. I had some problems there because the source provided wasn’t matching the binaries.. But it was enough to understand the reason for the error in most cases. Did you try that?

  5. commando Says:

    i am make project using openid. are you send for me document openid4java ?
    thank verry much

  6. tuan Says:

    i am student. i make project use openid. are you can send for me document openid4java ? thanks

  7. Aviad Says:

    @tuan/commando (suppose you’re the same person): I don’t understand the request. In any case, openid4java is not a project I maintain nor contribute to – I just used it with GWT, and wrote about the experience.

  8. Richard Says:

    Hey Aviad:

    Thanks for this writeup. Where is the code for:

    import util.HttpCookies;
    import util.DependencyInjection;

    Thanks,
    Richard.

  9. Aviad Says:

    @Richard: Did you look in the libraries I referenced to in my implementation notes’ section? These should be found there.

  10. Richard Says:

    Hey Aviad:

    I cannot find code for the following:

    import util.HttpCookies;
    import util.DependencyInjection;

    Thanks,
    Richard.

  11. Aviad Says:

    @Richard:

    I didn’t have the space to include the code for everything. Both classes are just placeholders I used in the code I published, since they do something “simple”:

    HttpCookies is a utility class which gets, sets and resets cookies on the user’s session. It works with the HttpServletRequest.getCookies and HttpServletResponse.addCookie methods.

    DependencyInjection is a utility class which eventually works with Guice – see my comments on the implementation’s detail regarding dependency injection.

  12. karthik Says:

    hi,

    I’m trying to implement OpenID in our gwt application using Eclipse IDE.
    Is it possible to implement it in hosted Mode?
    I cant get clear idea abt this implementation…..
    I need ur help …..

    i have lot of doubts.
    if possible mail me @
    inform.mail.kk@gmail.com.

  13. karthik Says:

    Hi Aviad,

    Help me ASAP.
    Its urgent for our project..

    Thanks in Advance.

    Karthik

  14. Aviad Says:

    @karthik: Sorry for the delay. Yes, it can be done in hosted mode, however note that the servlet does require you to play a bit with the host – notice notes 3 and 4 in my implementation notes.

  15. Karthik Says:

    Hi,

    Thanks for your reply.
    I got all your Code for Implementing OpenID , but i couldn’t find the 2 classes
    util.HttpCookies & util.DependencyInjection.

    I have a bit confusion in developing the codes for these classes.
    So I need your help aviad.
    If possible send the above mentioned classes to my mail…..

    Hope u will Help me to solve this issue…
    As we are currently doing openID I’ll be thankful if you send the code ASAP.

    Karthik

  16. karthik Says:

    Hi Aviad,

    We waiting for your reply..
    Its very urgent requirement for our project.
    Help me ASAP.

    Karthik

  17. asianCool Says:

    can you provide a demo workable example on this? preferable spring as injection

  18. Aviad Says:

    @asianCool: I already did provide a working demo. It lacks two classes that you need to implement (I explain how on the post). If you have any problem with the implementation, let me know.

  19. asianCool Says:

    can you show demo how gwt client call the servlet? is there a required to create composite “form” and do post?

  20. asianCool Says:

    in the gwt client code, when seCookie and doGet to call the servlet, may i know what values you set for

    “app-openid-auth”;
    “app-openid-name”;
    “app-openid-identifier”
    “app-openid-uniqueid”;

  21. asianCool Says:

    i know how to use the servlet already. i able to login using yahoo openid and myopenid.com , but cannot for google i enter url like below

    http://localhost:8888/openid?app-openid-auth=true&app-openid-name=user@gmail.com

    and get exception 0xa00: Authentication cannot continue: no discovery information provided.

  22. Aviad Says:

    @asianCool: I’m sorry, but I’m not sure about specific OpenID providers. It sounds to me like the gmail.com domain did not return any discovered OpenID services, i.e. they don’t support OpenID. It sounds likely, too.

    You might want to try http://openid-provider.appspot.com/ for Google accounts support, but I haven’t tried it myself.

  23. Aviad Says:

    Or even better, try this: http://code.google.com/apis/accounts/docs/OpenID.html

  24. asianCool Says:

    1.in verify method after

    if (id != null) {
    HttpCookies.setCookie(request, response, openIdCookieName, id.getIdentifier());

    callback.saveIdentifierForUniqueId(HttpCookies.getCookieValue(request, uniqueIdCookieName), id);
    }

    response.sendRedirect(callback.getLoginURL());

    is called, gwt page is re-render. how to use gwt to grab user information?

    2. how to call getRequestUserInfo() method in gwt?

  25. Aviad Says:

    @asianCool: After the OpenID authentication, the authentication token is in the cookie. Using your local DB you can link between that and his user information.

  26. lma Says:

    Hi Aviad,

    I’m also interested in the code for

    util.HttpCookies;
    util.DependencyInjection;

    I’m new with OpenId and I’d like to see how does this code look like. Do you have any problem to mail it to xxx… thank you!

    lma

  27. Aviad Says:

    @lma: I’ll try to mail it to the address you specified. For your own safety from spam etc, I removed the email address you wrote in the comment itself..

  28. beanryu Says:

    Hi Aviad, thank you for posting the code. Could you please tell me how should the client talk to the servlet(I assume the code you posted is the “servlet”). Should I use GWT’s RPC or GWT’s Request and Response class? Or maybe something else? What data should be sent to the Servlet from the client? Thank you so much Aviad.

  29. antony.trupe Says:

    I don’t quite follow the internal Callback interface. Where is it implemented?

  30. Aviad Says:

    @antony.trupe: The Callback interface is implemented in your server. It is where you add your business logic for how to store your users and how to reach your login screen (so that the servlet could make the redirections correctly).
    @beanryu: The servlet is a stand-alone servlet. I made it implement GWT’s RemoteService so that I could use it in a GWT environment, but it’s not necessarily what you should do.

  31. beanryu Says:

    Hi Aviad, I am using openid4java-0.9.5.593, for some reason, i got the following error:
    java.security.AccessControlException: access denied (java.lang.RuntimePermission modifyThreadGroup)… at the ConsumerManager.discover method, have you experienced this and found a solution? I googled it and read about it, people usually get this on GAE, but I am getting it in my local hosted mode. I am supplying the method with the string “http://myname.myopenid.com/”. Thank you for your help.

  32. caster Says:

    Take a look at the openid java library at http://code.google.com/p/dyuproject/
    Its small and easier to deploy … and works on appengine without problems

  33. beanryu Says:

    I looked at dyuproject before I tried using openid4java. The code in the quickstart link looks easy, but I have completely no idea how to get it to work. Also, I have no idea on where I can find out what each method does except looking directly at the source code and try to figure it out myself.

  34. simonswb6 Says:

    I am struggling a little with the client side piece. Are you using a straight rpc call to first get the openid url and then do the redirect? do you have an example how you would call the servelet from the gwt client?

  35. Aviad Says:

    @simonwb6: From a GWT client I don’t do anything special; from the GWT server, if the user requested a page which requires authentication in order to view it, I redirect the request to the servlet.

  36. Fenton Says:

    Hello Aviad,

    I tried to implement your solution, but I am confused: Where do you get the HttpServletReqeust and Response from? I saw that they are required as parameters in OpenID but I was not able to determine where these variables should come from in GWT.
    Thanks for your reply :) !

    Best regards

  37. Random Links #184 | YASDW - yet another software developer weblog Says:

    [...] Using OpenID within GWT I thought this would be easier [...]

  38. Draško Says:

    There is a much simpler way to use OpenID on Google AppEngine. Just use the following API:

    UserService userService = UserServiceFactory.getUserService();

    to get the authenticated user:

    User user = userService.getCurrentUser();

    to get the logout redirect URL:

    String redirectURL = userService.createLogoutURL(destinationURL, authDomain);

    to get the login redirect URL:

    String redirectURL = userService.createLoginURL(destinationURL, authDomain, federatedIdentity, attributesRequest);

    I have successfully used the following request attributes:

    Set attributesRequest = new HashSet();
    attributesRequest.add(“openid.mode=checkid_immediate”);
    attributesRequest.add(“openid.ns=http://specs.openid.net/auth/2.0″);
    attributesRequest.add(“openid.return_to=http://super-easy.appspot.com”);

    A working example with the GWT widget to select an OpenID provider is
    running under the http://super-easy.appspot.com

    HTH
    Draško

  39. Draško Says:

    There is a much simpler way to use OpenID on Google AppEngine.
    Just use the following API:

    UserService userService = UserServiceFactory.getUserService();

    to get the authenticated user:

    User user = userService.getCurrentUser();

    to get the logout redirect URL:

    String redirectURL = userService.createLogoutURL(destinationURL, authDomain);

    to get the login redirect URL:

    String redirectURL = userService.createLoginURL(destinationURL, authDomain, federatedIdentity, attributesRequest);

    I have successfully used the following request attributes:

    Set attributesRequest = new HashSet();
    attributesRequest.add(“openid.mode=checkid_immediate”);
    attributesRequest.add(“openid.ns=http://specs.openid.net/auth/2.0″);
    attributesRequest.add(“openid.return_to=http://super-easy.appspot.com”);

    A working example with the GWT widget to select an OpenID provider is
    running under the http://super-easy.appspot.com

    HTH
    Draško

  40. GWT笔记 User Authentication | 吃饭博客 Says:

    [...] http://chaoticjava.com/posts/using-openid-within-gwt/ [...]

  41. Dun74 Says:

    Thank you very much for this article,
    I used it in a sample projet, and it worked.
    Now I just don’t get how you retrieve the informations for the authenticated user you present in the response popup widget.
    I read carfully the code, and parsed the http response but I can find the email for example or the full name.
    Do I have to add parameters tu the request ?

    Dun

  42. Dun74 Says:

    Ok sorry, it was very easy :

    // Attribute Exchange example: fetching the ‘email’ attribute
    FetchRequest fetch = FetchRequest.createFetchRequest();
    fetch.addAttribute(“email”, “http://schema.openid.net/contact/email”,true);
    // obtain a AuthRequest message to be sent to the OpenID provider
    AuthRequest authReq = this.manager.authenticate(discovered, this.callback.getOpenIdServletURL(), null);
    authReq.addExtension(fetch);

    Thank you
    Dun

  43. Jesse Taylor Says:

    Did you ever write that post on how you unit tested OpenID?

    There is a question on Stack Overflow trying to figure out how to test it from PHP/Zend, but I feel like your tests might be useful in understanding how to implement it in PHP.

    Here’s where the question is: http://stackoverflow.com/questions/5258954/how-do-i-test-login-using-openid-in-zend-framework

  44. Aviad Says:

    @Jesse: Well, as I wrote, I used the Cactus testing framework for it.. Since Cactus creates both client and server sides during the test, and then re-routes the requests and responses through the testing framework, it was a bit tricky to set up – but I guess it wasn’t a special case for OpenID but a case about how to use Cactus properly. Did you check that framework?

  45. tim Says:

    I still don’t understand how it work….GWT has 1 “main page”. how can you restrict access to that page? If i go to myapp.appspot.com, the authentication servlet is not called.

    /Home.html

    *

    If I do this

    /*

    *

    then I get a redirect infinite loop problem when accessing myapp.appspot.com….