OpenID for Java Web applications, Part 1: Enable your Java Web applications to use OpenID authentication

OpenID is a decentralized authentication protocol that makes it easier for users to access resources in your Java™ Web applications. In this first half of a two-part article, you'll learn about the OpenID Authentication Specification and walk through the steps of incorporating it into a sample Java application. Rather than implement the OpenID Authentication specification by hand, author J. Steven Perry uses the openid4java library and a popular OpenID provider, myOpenID, to create a safe and reliable registration process for a Java application written in Wicket.

Share:

J Steven Perry, Principal Consultant, Makoto Consulting Group, Inc.

Photo of J Steven PerryJ. Steven Perry is an independent software development consultant and has been developing software professionally since 1991. Steve has a passion for software development, and enjoys writing about software development and mentoring other developers. He is the author of Java Management Extensions (O'Reilly) and Log4j (O'Reilly), and Joda-Time (which he wrote for IBM developerWorks). In his spare time he hangs out with his three kids, rides his bike, and teaches yoga. Steve is the owner and principal consultant for Makoto Consulting Group, located in Little Rock, Arkansas.



16 March 2010 (First published 27 January 2010)

Also available in Chinese Russian Japanese

OpenID is a decentralized authentication mechanism. Using OpenID, I can prove I own a URI such as http://openid.jstevenperry.com/steve, and I can use that identity to authenticate myself with any site that supports OpenID — such as Google, Slashdot, or Wordpress. Clearly, Open ID is great for end users. But using it got me to thinking: "What about using OpenID to create a standard, reliable authentication system for the Java-based Web applications I write for my customers?"

In this two-part article I will show you how to use the openid4java library and a well-known OpenID provider, myOpenID, to create an authentication system for a Java-based Web application. I'll also show you how to receive user information with an OpenID Simple Registration Extension (SReg).

I'll start by explaining what OpenID is and showing you how to get an OpenID of your own. Next, I will present a brief overview of how OpenID authentication works. Finally, I will walk through the steps involved in performing OpenID authentication using openid4java. In the second half of this article, you'll learn how to create your own OpenID provider.

Throughout the discussion I'll be working with a Wicket-based Java Web application that I wrote specifically for this article. You can download the source code for the application any time. You also might want to take a look at the openid4java library (see Resources).

Note: This article focuses on using OpenID for Java Web applications, but OpenID works in any software architectural scenario.

Introduction to OpenID

OpenID is a specification for proving a user owns an identifier. For now, just think of an identifier as a String that uniquely identifies a user. If you're like me, you own many identifiers or userids. I have a userid at Facebook, another at Twitter, and others at dozens of sites that I use around the Internet. I always try to use the same userid but it's not available on every new site I sign up for. So, I have a mental map of all of my userids and the Web sites they're associated with. What a pain; I use the "Forget your password?" feature a lot! It would be great if there were a way to claim a single identifier and use it everywhere.

OpenID solves exactly this problem. Using OpenID, I claim an identifier and use it on any site or Web resource that has adopted the protocol. The latest figures (from the OpenID Web site) say that more than 50,000 Websites support OpenID, including Facebook, Yahoo!, Google, and Twitter.

OpenID authentication

OpenID authentication is at the heart of OpenID, and consists of three main concepts:

  • The OpenID Identifier: A String of text that uniquely identifies the user.
  • The OpenID Relying Party (RP): An online resource (probably a Web site, but it could be a file, an image, or pretty much anything you want to control access to) that uses OpenID to identify who can access it.
  • The OpenID Provider (OP): A site where users can claim an OpenID and subsequently sign-in and authenticate their identity for the benefit of any RP.

The OpenID Foundation is a consortium whose members are interested in promoting open source identity management through the OpenID specification.

How does OpenID work?

Suppose a user is attempting to access a resource that is part of an RP's Web site, and the RP uses OpenID. To access the resource, the user must present his OpenID in a form that can be recognized (normalized) as an OpenID. The OpenID is encoded with the OP's location. The RP then takes the user's identifier and redirects the user to the OP, where he will be required to prove his claim to that ID.

Let's briefly consider each component of the OpenID specification and its role in this process.

OpenID Identifiers

At the heart of OpenID is, of course, the OpenID Identifier. An OpenID Identifier (or just "identifier") is a human-readable String of characters that uniquely identifies someone. No two users have the same OpenID, and that's what makes OpenID work. By following stipulations in the OpenID Authentication Specification Version 2.0, OpenID RPs are able to decode (or "normalize") an identifier to figure out how to authenticate a user. In the operational world of OpenID, where we as developers write code, two identifiers are of interest:

  • User-Supplied Identifier
  • Claimed Identifier

As the name suggests, a User-Supplied Identifier is the identifier supplied by the user to the RP. The User-Supplied Identifier must be normalized into a Claimed Identifier, which is just a fancy way to say that the identifier supplied by the user is transformed into a standard form. The Claimed Identifier can then be used to locate the OP through a process called discovery, after which the OP will authenticate the user.

OpenID Relying Party

It is normally the RP that is presented with a User-Supplied Identifier, which is normalized to a Claimed Identifier. The user's browser (the "User Agent") will be redirected to the OP so that the user can provide his or her password and be authenticated.

The RP neither knows nor cares about the specifics of how a Claimed Identifier is authenticated; it only wants to know whether the OP has successfully authenticated the user. If so, the User Agent (again, probably the user's browser) is forwarded to the secure resource that the user was attempting to access. If the user cannot be authenticated, then the RP denies access.

Open ID Provider (OP)

The OP, or OpenID Provider, is responsible for issuing Identifiers and performing user authentication. OPs also provide Web-based management of OpenIDs. OPs collect and hold the following basic information about each user:

  • E-mail address
  • Full name
  • Date of birth
  • Postal code
  • Country
  • Primary language

When an OP is asked to authenticate a Claimed Identifier, the user's browser is directed to a sign-in page where the user is challenged to enter his password. At that point, control is with the OP. If the user is successfully authenticated, then the OP directs the browser to a location specified by the RP (in a special "return-to" URL). If the user cannot authenticate, he will probably receive a message from the OP that his authentication attempt failed (at least that's the case with ClaimID and myOpenID, two popular OpenID Providers).

Becoming an OpenID Relying Party

So now you know about the major components of OpenID and how they fit together. For the remainder of the article, we'll focus on writing an OpenID Relying Party (RP) using the open source openid4java library.

The first step in using OpenID is to get an identifier. It's easy to do: just go to myOpenID and click the SIGN UP FOR AN OPENID button. Pick an OpenID like redneckyogi or jstevenperry (both of which are mine, by the way). The sign up form will tell you whether the userid you've chosen is already taken. If not, you'll be instructed to enter a password, an e-mail address, some text in a JCaptcha-style text box (you're not a bot, are you?), and that's it!

Some minutes later you'll get an e-mail at the address provided containing a link in it. Click the link to confirm your e-mail address and — congratulations! — you now have an OpenID!

Of course, as with any awesome technology there are numerous OpenID providers to choose from (see Resources for a complete list).

To illustrate how quick and easy it is to get an OpenID, I signed up with accounts at myOpenID, Verisign, and ClaimID in the space of about 30 minutes. And that includes time spent entering detailed information and uploading a picture!

You may already have an OpenID

According to OpenId.net, Google, Wordpress, and other popular sites support OpenID. If you've signed up for any of these sites you may already have an OpenID.

For instance, if you have a Yahoo! Account, you probably also have an OpenID (I did, and didn't even know it). You just use your Yahoo! ID when you sign in, and Yahoo is your OpenID Provider. You provide your Yahoo-based OpenID as whatever@yahoo.com and the RP will ask Yahoo to authenticate you (you can actually see this in action if you run the sample application that accompanies this article).

About the sample application

As I said at the beginning of this article, I've written a Java Web application that uses openid4java to create a simple OpenID Relying Party (RP). It is a simple application that you can build (as a WAR), drop into Tomcat, and run from your local machine. The sample application has a very tight focus:

  • The user enters her OpenID on a registration page.
  • The application verifies the Identifier (by directing the user to her OP to sign in).
  • Upon successful authentication, the application retrieves the user's profile information from the OP, and directs the user to a Save page where she can review and save her profile information.
  • The information displayed on the Save page is pulled from the information available from the OP.

I wrote the application with Wicket because, well, I really like Wicket. But I've tried to minimize Wicket's "footprint" so that it doesn't distract you from learning how to write an OpenID Relying Party.

The architecture of the sample application is divided into two areas of responsibility:

  • User interface written in Wicket
  • OpenID authentication — using the openid4java library

Of course the two areas intersect, but again, I have tried to keep overlap to a minimum to make it easier to follow the OpenID instructions, rather than getting distracted by the details of Wicket.

About openid4java and the sample application code

The OpenID Authentication spec is complicated. If you implement specifications all the time, you'll probably be very comfortable writing your own implementation. As for me, I'm lazy. I don't want to do any more work than I have to in order to solve the problem at hand, which is where the openid4java library comes into play. openid4java is an implementation of the OpenID Authentication specification that makes it much easier to use OpenID programmatically.

The code listings that follow show the openid4java API calls an RP makes to use OpenID. One thing you may notice is how little code the sample application actually needs to make this happen. openid4java really does make your life easier.

To reduce the Wicket footprint in the sample application, I've isolated the code that calls openid4java into its own Java class called RegistrationService (located in com.makotogroup.sample.model). This class contains five methods that correspond to the usage of the openid4java API:

  • getReturnToUrl() returns the URL that the browser will be directed to once successful authentication has taken place.
  • getConsumerManager() is used to obtain an instance of the main openid4java API class. This class handles all of the code the sample RP application needs to perform authentication.
  • performDiscoveryOnUserSuppliedIdentifier() does what its name implies: it handles any potential problems that arise during the discovery process.
  • createOpenIdAuthRequest() creates the AuthRequest construct that is required to do the authentication.
  • processReturn() handles processing the authentication request's results.

Writing the RP

The whole point of authentication is for the user to prove his or her identity. Doing this protects a Web resource from access by unwanted or malicious visitors. Once the user has proved his identity, you decide whether or not to grant him access to the resource (though authorization is beyond the scope of this article).

The sample application for this article performs a function common to many Web sites: user registration. It assumes that if the user can prove his identity then he is allowed to register. It's a simple premise, but it will demonstrate how a typical "conversation" with the OP goes and how to use openid4java to do it. Here are the basic steps:

  1. Obtain the User-Supplied Identifier: The RP gets the user's OpenID.
  2. Discovery: The RP normalizes the User-Supplied Identifier to determine which OP to contact for authentication and how to contact it.
  3. Association: An optional step, but one I highly recommend, wherein the RP and OP establish a secure communication channel.
  4. Authentication request: The RP asks the OP to authenticate the user.
  5. Verification: The RP requests userid verification from the OP and ensures the communication has not been tampered with.
  6. Proceed to application: Following authentication, the RP directs the user to the resource he or she initially requested.

Next, we'll look at each of these steps in detail, including code examples. As we progress through the sections below, I will use a single example to illustrate the OpenID authentication process from start to finish.

Obtain the User-Supplied Identifier

This is the job of your RP application. In the working example, the userid is obtained on the application's OpenIdRegistrationPage. I enter my OpenID and click the Confirm OpenID button. The sample application (which acts as the RP) now has my User-Supplied Identifier. Figure 1 shows a screen shot of the sample application in action.

Figure 1. Obtaining the User-Supplied Identifier
A screen shot of the sample application in action.

In this case, the User-Supplied Identifier is redneckyogi.myopenid.com.

The UI code is responsible for two things: making sure the user has entered text into the Your OpenID text box and submitting the form when the user clicks the Confirm OpenID button. Following confirmation, the application begins the call sequence. Listing 1 shows the code for the OpenIdRegistrationPage that submits the form and makes this call sequence.

Listing 1. Wicket UI code to make the OpenID authentication call sequence using RegistrationService.java

Click to see code listing

Listing 1. Wicket UI code to make the OpenID authentication call sequence using RegistrationService.java

Button confirmOpenIdButton = new Button("confirmOpenIdButton") {
  public void onSubmit() {
    String userSuppliedIdentifier = formModel.getOpenId();
   DiscoveryInformation discoveryInformation =RegistrationService.performDiscoveryOnUserSuppliedIdentifier(
        userSuppliedIdentifier);
    MakotoOpenIdAwareSession session =
     (MakotoOpenIdAwareSession)owningPage.getSession();
    session.setDiscoveryInformation(discoveryInformation, true);
    AuthRequest authRequest =
      RegistrationService.createOpenIdAuthRequest(
        discoveryInformation, returnToUrl);
    getRequestCycle().setRedirect(false);
   getResponse().redirect(authRequest.getDestinationUrl(true));
    }
};

Try not to get too distracted by the example and how it fits into the Wicket UI code (though if you're curious, feel free to look at OpenIdRegistrationPage.java, from which Listing 1 was taken). The important point here is that when the user clicks the button, the UI code delegates to the various methods of RegistrationService to call openid4java's API, doing three things (each of which is in bold in Listing 1):

  1. Perform discovery on the User-Supplied Identifier
  2. Create the openid4java AuthRequest object that will be used to make the authentication request
  3. Redirect the browser to the OpenID provider

After redirecting the browser, the UI code is done and control is in the hands of the OP. Notice that myopenid.com is part of the identifier and the User-Supplied Identifier is not a well-formed URL. Still, enough information is encoded in this identifier to allow openid4java to normalize and perform discovery on it. We will see that next.

Discovery

The RP takes the User-Supplied Identifier and converts it to a form that can be used to determine two things: who the OpenID Provider (OP) is and how to contact the OP.

The process of discovery is used by the RP to determine how to make requests of the OP, and the key is the User-Supplied Identifier. But before the User-Supplied Identifier can be used for discovery, it must be normalized. The openid4java library actually does the heavy lifting to normalize the User-Supplied Identifier, so there's no need to cover the details of it here.

The two distinct forms are:

  1. XRI: Extensible Resource Identifier
  2. URL: Uniform Resource Locator

In this article, we will look at URL examples. The User-Supplied Identifier from Figure 1 is a URI missing a scheme, so as part of normalization, openid4java attaches "http://" to it and arrives at the Claimed Identifier http://redneckyogi.myopenid.com.

Encoded in the Claimed Identifier is the name of the OP, in this case, myOpenID. Because the Claimed Identifier is a URL, openid4java knows how to contact the OP — at http://myopenid.com— which it does.

Listing 2 (from the sample application's RegistrationService class) shows how the RP uses openid4java to perform discovery.

Listing 2. Using openid4java to perform discovery
public static
   DiscoveryInformation performDiscoveryOnUserSuppliedIdentifier(
      String userSuppliedIdentifier) {
	
  DiscoveryInformation ret = null;
  ConsumerManager consumerManager = getConsumerManager();
  try {
    // Perform discover on the User-Supplied Identifier
   List<DiscoveryInformation> discoveries =
      consumerManager.discover(userSuppliedIdentifier);
    // Pass the discoveries to the associate() method...
    ret = consumerManager.associate(discoveries);
  } catch (DiscoveryException e) {
    String message = "Error occurred during discovery!";
    log.error(message, e);
    throw new RuntimeException(message, e);
  }
  return ret;
}

The class at the center of openid4java's approach to OpenID authentication is ConsumerManager. openid4java has strict guidelines about how this class is to be used. For this reason, it is stored as a static class member and accessed through the getConsumerManager() method (see RegistrationService.java in the sample application for more details).

In a single line of code (in bold in Listing 2) openid4java allows your code to normalize the User-Supplied Identifier and perform discovery on it. What is returned is a java.util.List of DiscoveryInformation objects. These can be treated as opaque objects. Just make sure to keep them because you will need them if your RP implementation chooses to form an association with the OP (as the sample application does).

Association

Association is a way for the RP and the OP to establish a shared secret (through Diffie-Hellman Key Exchange) to make their interactions more trusted and secure. Association is not required by the OpenID specification. Association is performed from the RP code with a single call to the associate() method on ConsumerManager, as shown in Listing 3.

Listing 3. Using openid4java to establish association
public static 
   DiscoveryInformation performDiscoveryOnUserSuppliedIdentifier(
      String userSuppliedIdentifier) {
	
  DiscoveryInformation ret = null;
  ConsumerManager consumerManager = getConsumerManager();
  try {
    // Perform discover on the User-Supplied Identifier
    List<DiscoveryInformation> discoveries =
      consumerManager.discover(userSuppliedIdentifier);
    // Pass the discoveries to the associate() method...
    ret = consumerManager.associate(discoveries);
  } catch (DiscoveryException e) {
    String message = "Error occurred during discovery!";
    log.error(message, e);
    throw new RuntimeException(message, e);
  }
  return ret;
}

This method returns the DiscoveryInformation object that describes the results of the discovery (you may treat this object as opaque). The sample application stores the DiscoveryInformation object in a session because it will be needed later, as you will see. This object is also required to make the authentication request, which we'll look at next.

Authentication

After the RP has successfully performed discovery on the User-Supplied Identifier, it's time to authenticate the user. ConsumerManager is asked to build a special object called AuthRequest that will be used by the OP to process the authentication request.

During this interaction, the OP will be asked to make use of an OpenID extension called SimpleRegistration (SReg for short); this extension enables the RP to request that certain attributes from the user's profile with the OP to be returned in the response. Listing 4 shows the code to build the AuthRequest object and request the attributes using SReg.

Listing 4. Building the AuthRequest and using the SReg extension
public static AuthRequest 
createOpenIdAuthRequest(DiscoveryInformation 
discoveryInformation, String returnToUrl) {
  AuthRequest ret = null;
  //
  try {
    // Create the AuthRequest object
   ret =getConsumerManager().authenticate(discoveryInformation,
                                            returnToUrl);
    // Create the Simple Registration Request
   SRegRequest sRegRequest = 
SRegRequest.createFetchRequest();
    sRegRequest.addAttribute("email", false);
    sRegRequest.addAttribute("fullname", false);
    sRegRequest.addAttribute("dob", false);
    sRegRequest.addAttribute("postcode", false);
    ret.addExtension(sRegRequest);
  } catch (Exception e) {
    String message = "Exception occurred while building " +
                     "AuthRequest object!";
    log.error(message, e);
    throw new RuntimeException(message, e);
  }
  return ret;
}

The first line in bold in Listing 4 shows the call to ConsumerManager.authenticate(), which doesn't actually make the authentication call. It simply takes the DiscoveryInformation object returned from a successful discovery interaction with the OP (see Listing 3), and the URL to which the User Agent (the browser) will be directed following successful authentication.

The second bold line shows how to create the SReg request through a static method call to SRegRequest.createFetchRequest(). Then the attributes you want returned from the OP as part of the Simple Registration Extension are requested through calls to addAttribute() on the SRegRequest object. Finally, the extension is added to the AuthRequest by calling addExtension().

openid4java makes all of these actions very intuitive. At this point, the browser is directed to the OpenID Provider that is responsible for authenticating the user, where the user will enter his or her password. See OpenIdRegistrationPage.java for the Wicket UI code that does the redirect. Figure 2 shows a screen shot of the myOpenID server authenticating a request.

Figure 2. myOpenID processing an authentication request
A screen shot of the myOpenID server authenticating a request.

At this point you need to make sure that you have code that can process a request running at the URL you specified as the "return-to" URL (see Listing 4). The return-to URL for the sample application is hard-coded in RegistrationService.getReturnToUrl(). The OpenIdRegistrationSavePage's constructor cracks the Web request to see whether or not it is a return from the OP. If the request is a return from the OP, then it must be verified.

Verification

Listing 5 shows the code to find out whether a request has come from the OP. If it has, there will be a parameter, is_return, whose value is true. If this is the case, then openid4java is used to verify the request (which is really a response from the OP) and pull out the attributes that you requested in Listing 4.

Listing 5. Handling the return-to URL
public OpenIdRegistrationSavePage(PageParameters pageParameters) {
  RegistrationModel registrationModel = new RegistrationModel();
  if (!pageParameters.isEmpty()) {
    String isReturn = pageParameters.getString("is_return");
    if (isReturn.equals("true")) {
      MakotoOpenIdAwareSession session = 
        MakotoOpenIdAwareSession)getSession();
      DiscoveryInformation discoveryInformation =
        session.getDiscoveryInformation();
      registrationModel = 
        RegistrationService.processReturn(discoveryInformation,
          pageParameters, 
          RegistrationService.getReturnToUrl());
      if (registrationModel == null) {
          error("Open ID Confirmation Failed.");
        }
      }
    }
    add(new OpenIdRegistrationInformationDisplayForm("form",
        registrationModel));
  }

In this code, the Wicket page's constructor first determines that the request is a response from the OP to the earlier authentication request. It uses a custom Session class (MakotoOpenIdAwareSession) to grab the DiscoveryInformation object, which was stored after the successful discovery interaction with the OP. The request is verified by the RegistrationService.processReturn() method using the DiscoveryInformation object, the request parameters, and the return-to URL. If the request is successfully verified, a fully populated RegistrationModel object is returned. This can act as the Wicket model for the OpenIdRegistrationSavePage, where the application can proceed with its intended function.

Proceed to application

If the response to the authentication request is successfully verified, the user is granted access to whatever resource was being protected by the RP through OpenID. In the case of the sample application, this is the process of registration. If the user is successfully authenticated, she is taken to a screen where she can review the information pulled from the OP, change it if necessary, and save it. The sample application doesn't contain code to actually save the registration information, but the hooks are there. Figure 3 shows the information pulled from the OP when I run the sample application to authenticate my OpenID.

Figure 3. Sample application showing profile information pulled from the OP
Screen shot showing information pulled from the OpenID Provider for authentication purposes.

Conclusion

OpenID solves the problem of keeping up with numerous online identities and is gaining widespread adoption as a reliable identity management solution. Obtaining your own OpenID is easy and millions have already been claimed. Like any specification, OpenID Authentication is complex, but openid4java greatly simplifies it. In this article, you've seen how OpenID authentication works. You've also learned how easy it is to incorporate into a Java Web application using openid4java.

In Part 2 of this article, we'll focus on the other half of the OpenID puzzle: writing an OpenID Provider. It will be another code-driven discussion with a sample Java Web application written specifically for the purpose of instruction. In the meantime, please feel free to use the code from RegistrationService.java in order to implement OpenID authentication in your Java Web applications. Go ahead, I don't mind!


Download

DescriptionNameSize
OpenID examplesopenid4java-sample-app.zip4.3 MB

Resources

Learn

Get products and technologies

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology, Open source
ArticleID=464953
ArticleTitle=OpenID for Java Web applications, Part 1: Enable your Java Web applications to use OpenID authentication
publish-date=03162010