OpenID is gaining widespread adoption as a reliable identity management and authentication solution that enables end users to access Web sites and other online resources using one universally recognized user ID. In Part 1, I introduced the OpenID Authentication specification and showed you how to incorporate it into a Java Web application using the openid4java library implementation.
For most of that article we focused on the OpenID Relying Party (RP), which is an online resource such as a Web site or MP3 that uses OpenID for registration and authentication. The other half of the OpenID Authentication specification is the OpenID Provider (OP). The OP assists users in claiming an OpenID and authenticates users attempting to login to OpenID-compliant Web resources.
Plenty of OpenID Providers already exist (including myOpenID, which was the OP for the Java Web application registration system we explored in Part 1), and in most cases there's no need to reinvent the wheel.
One scenario where it could make sense to build your own OP is an application cluster where several applications share resources in a network of trust. In that case, you might want to create a secure, "closed loop," system. It would be convenient to let the user sign in for all of the applications at once, rather than having to sign in to each application separately. Having one application in the cluster act as the OP would enable you to set up single sign-on authentication for all of the applications.
In this article, we'll focus on writing an OpenID Provider to secure a number of applications in a closed loop architecture. We'll start with a closer look at the benefits and structure of single-sign-on authentication, then walk through the coding of a simple OpenID Provider for a cluster architecture. Once again, we'll use the openid4java library to provide the core runtime capabilities for the authentication system and to ensure that our OpenID Provider is compliant with the OpenID Authentication specification.
In certain enterprise scenarios, it makes more sense to combine applications that have different functionality and core competencies than to attempt to build all of the functionality into a single application. Such application clusters are often at the heart of B2B, where each partner brings something to the table that increases the value of a business proposition as a whole.
The challenge in developing such a cluster is authentication; it doesn't work to have each application authenticate the end user separately, at least not from the end-user's perspective.
In a clustered system that uses the OpenID standard for authentication, each partner application delegates authentication to the OP. Each application is assured that access to its functionality and resources is secure, and end users have the convenience of signing in just once per session.
Let's take a closer look at the players involved in a single sign-on authentication system. Note that the architecture discussed below builds on the sample application developed in Part 1.
An OpenID Relying Party, as you'll recall, is a Web site or other online resource that requires secure access to its contents. An RP uses an OpenID Provider (OP) to authenticate users. The RP may also utilize the Simple Registration (SReg) and/or Attribute Exchange (AX) extensions (see Resources) to request registration or identifying information about users. The RP makes SReg and AX requests when it asks the OP to authenticate the user through calls to the openid4java library.
The OpenID Provider provides authentication to all partner applications. Once the user is successfully authenticated through calls to the openid4java library, the OP satisfies SReg and AX requests from the RP. The OP sits at the center of the single sign-on architecture that we'll explore in this article.
In the previous article, you saw how to use openid4java to write a Relying Party for a Java Web application registration system. In this article, we'll follow a similar procedure for the OpenID Provider. openid4java makes it a snap to adhere to the OpenID Authentication specification because all of the OpenID infrastructure has been coded already.
The purpose of the sample application is to show how the OpenID RP and OP work together to protect a resource from unauthorized access. The sample application has a very tight focus:
- The user attempts to access a protected resource.
- The RP asks the OP to authenticate the user.
- The OP authenticates the user, if he or she isn't already logged in.
- The RP determines whether the logged-in user has authorization to access the protected resource.
The sample application includes code for both the RP and the OP so that you can see how they work together. In a real-world scenario you wouldn't deploy the two components in the same application — no point to that! — but seeing them together and studying how they interact should be helpful.
Code listings in the sample app
The code listings in the sections that follow show the openid4java API calls an OP (and RP) makes to use OpenID. One thing you may notice is how little code the sample application actually requires. openid4java really does make your life easier. The code that the RP uses is essentially just like the code you saw in Part 1, so I refer you there for more information about RP internals. I will point out the few minor differences (mostly related to AX, which I didn't show in Part 1) as we go along.
Like the application I wrote for the first article, this one also uses Wicket as the UI. To reduce the Wicket footprint in the sample application, I've isolated the code that the OP uses to call openid4java into its own Java class, called OpenIdProviderService (in com.makotogroup.sample.model).
OpenIdProviderService.java contains several methods that correspond to the usage of the openid4java API:
- getServerManager() configures and returns a reference to the openid4java
ServerManagerclass. - getOpEndpointUrl() returns the Endpoint URL where the OP receives requests from the RP.
- processAssociationRequest() uses openid4java to associate the OP at the RP's request.
- sendDiscoveryResponse() sends a discovery request to the RP.
- createAuthResponse() creates the openid4java
AuthResponsemessage that is sent to the RP following an authentication request. - buildAuthResponse() is the core method that handles OpenID Simple Registration and Attribute Exchange requests.
To start up the sample application, run Ant [REF] and build
the WAR target, then copy it to your Tomcat webapps directory and start Tomcat.
OpenID authentication: Step by step
When a user attempts to access a protected resource from a Relying Party (RP), the RP makes sure the user is who he says he is (authentication) and then decides whether or not to grant him access (authorization). The focus of this article is authentication, so if the user is authenticated by the OpenID Provider (OP), the sample application will grant access to the protected resource. In a real-world scenario, the RP would also perform some kind of authorization.
When you run the sample application, you will see a screen with a protected resource on it. The following sequence of events will occur, which I will describe in more detail in the next sections:
- Request to access a protected resource: The user attempts to access a protected resource on the RP's Web site.
- RP performs discovery: The RP sends a discovery request to the OP in order to establish a connection and perform association.
- OP responds to discovery request: The OP responds correctly to a discovery request by sending back an XRDS (eXtensible Resource Descriptor Sequence) via the SReg, Attribute Exchange (AX), or OpenID Provider Authentication Policy (AP) extension (which I won't cover here; see Resources). The XRDS confirms the OP as the user's OpenID service provider.
- RP requests user authentication: The RP checks with the OP to see if the user can be authenticated. If the login is successful, the RP uses SReg, AX or both extensions to request certain information about the user.
- OP authenticates user: If the user is not logged in or has an invalid session, he is asked to provide his login credentials. If authentication is successful, the OP notifies the RP and sends along any data requested through SReg and/or AX.
- RP grants access: The user is granted access to the protected resource. In a real-world scenario, most RPs would check a user's authorization before granting access.
We'll take a look at each step in detail below.
Request to access a protected resource
The sample application contains a single protected resource. When the application starts and you access the RP URL, http://localhost:8080/openid-provider-sample-app/, the following page loads:
Figure 1. The sample application's main page
When a user clicks the link, the code in Listing 1 executes:
Listing 1. The application main page that contains the protected resource
package com.makotogroup.sample.wicket;
. . .
public class OleMainPage extends WebPage {
public OleMainPage() {
add(new OleMainForm("form"));
}
public class OleMainForm extends Form {
public OleMainForm(String id) {
super(id);
add(new PageLink("openIdRegistrationPage", new IPageLink() {
public Page getPage() {
return new OpenIdRegistrationPage();
}
public Class<? extends WebPage> getPageIdentity() {
return OpenIdRegistrationPage.class;
}
}));
}
}
}
|
Notice the code in bold in Listing 1. When the user clicks the link shown in Figure 1, Wicket takes him or her to the OpenIdRegistrationPage (the resource). At this point, the destination of the link is invoked, which runs the constructor of the OpenIdRegistrationPage class. This class does two things:
- Acts as the point of entry for an initial call.
- Acts as a "callback" from the OP following successful authentication.
When the initial call is made to access this page, no Wicket PageParameters are passed in and the RP knows it needs to check with the OP to authenticate the user.
For the RP and OP to communicate, the RP must perform discovery on the OP. This is simple from a coding standpoint (again, openid4java to the rescue), but it's an important step so I've broken it out here.
Here is the code the RP uses (from the constructor of OpenIdRegistrationPage) to send the discovery request:
DiscoveryInformation discoveryInformation =
RegistrationService.performDiscoveryOnUserSuppliedIdentifier(
OpenIdProviderService.getOpEndpointUrl());
|
In that code, the RP does two things:
- Performs discovery on the OP's Endpoint URL.
- Associates itself with the OP. (See Part 1 for a detailed explanation of Diffie-Hellman Key exchange and some of the other cool things that happen during association.)
Next, it's up to the OP to handle the RP's discovery request.
OP responds to discovery request
Remember that openid4java is running on both the RP and the OP sides of the sample application. So, as part of the discovery process with the OP, the RP side of openid4java sends an empty request to the OP's Endpoint URL. The Endpoint URL is where the OP is contacted, and where it receives all requests from the RP. The OP must be set up to handle this request. If you look inside OpenIdProviderService.getOpEndpointUrl(), you will notice that the Endpoint URL is
http://localhost:8080/openid-provider-sample-app/sample/OpenIdLoginPage.
When the RP sends its empty request to the OP, Wicket constructs the OpenIdLoginPage and runs its constructor, shown in Listing 2:
Listing 2. The OP entry point
public OpenIdLoginPage(PageParameters parameters) throws IOException {
super(parameters);
if (parameters.isEmpty()) {
// Empty request. Assume discovery request...
OpenIdProviderService.sendDiscoveryResponse (getResponse());
. . .
|
Notice that if the OP receives an empty request, it assumes it is a discovery request. It then creates and sends back an XRDS document to the requester.
Listing 3 shows the code for sendDiscoveryRequest():
Listing 3. Sending back the response to a discovery request
public static void sendDiscoveryResponse (Response response) throws IOException {
//
response.setContentType("application/xrds+xml");
OutputStream outputStream = response.getOutputStream();
String xrdsResponse = OpenIdProviderService.createXrdsResponse();
//
outputStream.write(xrdsResponse.getBytes());
outputStream.close();
}
|
The XRDS document is vital for the RP-side of openid4java to function correctly. For the sake of brevity, I've left the details of the document out of this article; download the sample application source code for details.
When the RP receives the XRDS document from the OP, it knows it has contacted the correct OP for that user. The RP then creates and sends the authentication request to the OP.
RP requests user authentication
The RP checks with the OP to ensure that the user can be authenticated. The series of calls is shown in Listing 4 (from the constructor):
Listing 4. RP code to delegate authentication to the OP
DiscoveryInformation discoveryInformation =
RegistrationService.performDiscoveryOnUserSuppliedIdentifier(
OpenIdProviderService.getOpEndpointUrl());
MakotoOpenIdAwareSession session =
(MakotoOpenIdAwareSession)getSession();
session.setDiscoveryInformation(discoveryInformation, true);
AuthRequest authRequest =
RegistrationService.createOpenIdAuthRequest(
discoveryInformation,
RegistrationService.getReturnToUrl());
getRequestCycle().setRedirect(false);
getResponse().redirect(authRequest.getDestinationUrl(true));
|
First, the RP contacts the OP at its Endpoint URL. This call might look strange, but remember that in this scenario the focus is on a cluster of applications using a trusted partner to act as the OP. From the RP's point of view, authenticating the user-supplied identifier is nothing more than discovering the whereabouts of the OP and letting openid4java construct the necessary objects to make subsequent interactions smooth. The OP will handle the mechanics of authentication.
Next, the current Wicket Session is obtained and DiscoveryInformation obtained from openid4java is stored there for later use. I wrote a special Session subclass called MakotoOpenIdAwareSession to it easier to store openid4java objects in Session.
After that, an authentication request is created using the DiscoveryInformation object obtained from openid4java. This object is used to tell Wicket where to redirect to perform the authentication call.
All of the steps above should be familiar from Part 1. I repeat them here because the architecture of the sample application code used in this article is significantly different from the code in Part 1. I also want you to see the OP side of the API calls, and to be able to pull them together.
At this point, the RP is awaiting an authentication response from the OP. Before proceeding to the next step, let's take a closer look at the role of Attribute Exchange in authenticating the user.
The OpenID Attribute Exchange extension
In the first half of this article, we looked briefly at the Simple Registration (SReg) extension, which makes it possible to exchange a specific set of information (defined by the SReg spec) between RP and OP. If you look at the createOpenIdAuthRequest() method in this article's sample application, you'll notice that the RP uses another extension, OpenID Attribute Exchange (AX), to request information from the OP.
Like the SReg extension, OpenID Attribute Exchange (AX) is used to exchange information between the RP and the OP in a consistent, standard way. Unlike SReg, AX lets OpenID relying parties and providers exchange unlimited information, provided that both the RP and OP support the AX extension.
Briefly, the RP asks the OP for specific information through a message, and the OP sends that information back in a message. These messages are encoded in the URL that the browser is redirected to, but openid4java uses objects to make the information available.
The RP uses a class called FetchRequest to make an AX
request. Once it has a reference to this message object, it adds the attributes it wants to be returned from the OP, as shown in Listing 5:
Listing 5. The RP's FetchRequest with attributes
AuthRequest ret = obtainSomehow();
// Create AX request to get favorite color
FetchRequest fetchRequest = FetchRequest.createFetchRequest();
fetchRequest.addAttribute("favoriteColor",
"http://makotogroup.com/schema/1.0/favoriteColor",
false);
ret.addExtension(fetchRequest);
|
When the OP sends back information to the RP, it uses the same constructs, as shown in Listing 6:
Listing 6. The OP sends back the requested attributes
if (authRequest.hasExtension(AxMessage.OPENID_NS_AX)) {
MessageExtension extensionRequestObject =
authRequest.getExtension(AxMessage.OPENID_NS_AX);
FetchResponse fetchResponse = null;
Map<String, String> axData = new HashMap<String, String>();
if (extensionRequestObject instanceof FetchRequest) {
FetchRequest axRequest = (FetchRequest)extensionRequestObject;
ParameterList parameters = axRequest.getParameters();
fetchResponse = FetchResponse.createFetchResponse(
axRequest, axData);
if (parameters.hasParameter("type.favoriteColor")) {
axData.put("favoriteColor", registrationModel.getFavoriteColor());
fetchResponse.addAttribute("favoriteColor",
"http://makotogroup.com/schema/1.0/favoriteColor",
registrationModel.getFavoriteColor());
}
authResponse.addExtension(fetchResponse);
} else {
// ERROR
}
}
|
Each attribute that is defined has both a simple name and a URI associated with it. In this case, the attribute's simple name is FavoriteColor and its URI is http://makotogroup.com/schema/1.0/favoriteColor.
Furthermore, the attribute must be capable of being stringified (see the sample application for an example of sending a date field in this manner). When you define attributes to exchange between RP and OP both sides have to agree on the attributes that are exchanged; beyond that, the sky's the limit!
Now let's pick up where we left off with the application interactions.
Where we left off, the authentication request had arrived at the OP's Endpoint URL. Next, the OP will crack the request to determine what to do next. The OP opens the request to obtain its mode, which may be association or authentication.
Listing 7. OP processes the association request
//From (OpenIdLoginPage's constructor):
public OpenIdLoginPage(PageParameters parameters) throws IOException {
super(parameters);
. . .
if ("associate".equals(mode)) {
OpenIdProviderService.processAssociationRequest(getResponse(), requestParameters);
}
. . .
}
//From (OpenIdProviderService):
public static void processAssociationRequest(Response response, ParameterList request)
throws IOException {
Message message = getServerManager().associationResponse(request);
sendPlainTextResponse(response, message);
}
private static void sendPlainTextResponse(Response response, Message message)
throws IOException {
response.setContentType("text/plain");
OutputStream os = response.getOutputStream();
os.write(message.keyValueFormEncoding().getBytes());
os.close();
}
|
In Listing 7, OpenIdLoginPage's constructor (which is the entry point for the OP in the sample application) first cracks the request. The mode indicates an association request, so it delegates the mechanics of association to openid4java, whose code is wrapped in OpenIdProviderService.java. The association request is sent back to the RP.
Once the RP is satisfied that association has been established (actually, once openid4java is satisfied) the RP makes another call into the OP. Again the OP cracks the request and processes it. Most of the time this will be a checkid_authentication request.
Listing 8 shows the code from OpenIdLoginPage's constructor:
Listing 8. openid4java cracks the checkid_authentication request
public OpenIdLoginPage(PageParameters parameters) throws IOException {
super(parameters);
. . .
else if ("checkid_immediate".equals(mode)
||
"checkid_setup".equals(mode)
||
"check_authentication".equals(mode)) {
if (((MakotoOpenIdAwareSession)getSession()).isLoggedIn()) {
// Create AuthResponse from session variables...
sendSuccessfulResponse();
}
add(new OpenIdLoginForm("form"));
. .
}
|
Note the two lines in bold in Listing 8. In the first bold line, OpenIdLoginPage sends back a successful request. First, OpenIdLoginPage uses a Session object to determine whether or not the user is logged in (a logged-in user should not have to log in again). If the user is logged in, it returns a successful authentication message. This is how single sign-on is accomplished in the sample application.
If the user is not logged in, then Wicket creates a login form where the user can enter his or her credentials, as shown in Figure 2:
Figure 2. The OP displays a login screen to an unauthenticated user
If the user is successfully authenticated he is sent back a successful response, and
some information — specifically the DiscoveryInformation object — is stored in Session. Again, these are the underlying mechanics of single sign-on authentication.
At this point, the browser is redirected to the RP's "return-to" URL with a successful authentication response.
If the user has successfully logged in, the OP sends back a successful AuthResponse message. It is now up to the RP to grant access to the user. The sample application automatically grants access if the user is authenticated by the OP. In addition, the OP sends all of the information that the RP has requested about the user. In Figure 3, the RP displays the information on its registration screen:
Figure 3. The sample app's registration page showing information retrieved from the OP
In this article, you've seen how to use the OpenID Authentication specification to set up single sign-on user authentication for a cluster of partner applications. A single sign-on architecture works when trust has been established between the partner applications, and where one partner acts as the OpenID Provider (OP).
Using OpenID for authentication and data exchange ensures that all participating parties have agreed to the terms of authentication and authorization. Because OpenID is a widely adopted standard, new adopters will also find considerable industry support for learning about and troubleshooting OpenID Authentication implementations.
Take a look at the sample code if you want to learn more about the single sign-on architecture implemented in this article. Just WAR it up, drop it into Tomcat, and run it! Be sure to turn on TRACE logging and watch the log output, which reveals details of the application not discussed in the article.
Like any specification, OpenID Authentication is complex, but openid4java makes using it very simple. Feel free to download the source code from this article and use it to assist in authenticating your applications with OpenID.
| Description | Name | Size | Download method |
|---|---|---|---|
| Source code for the sample application | openid-provider-sample-app.zip | 4.5KB | HTTP |
Information about download methods
Learn
- "OpenID for Java Web Applications, Part 1" (Steve Perry, developerWorks, January 2010): Get started with the OpenID Authentication specification and openid4java.
- "Protect your Project Zero and WebSphere sMash applications with OpenID" (Todd Kaplinger and Gang Chen, developerWorks, February 2008) shows you how to secure a WebSphere sMash application with OpenID.
- No better way to learn more about OpenID than to read the OpenID Authentication specification.
- OpenID Attribute Exchange (AX) is an OpenID extension for passing unlimited user information between an RP and OP.
- Also see the specifications for the OpenID Simple Registration (SReg) and OpenID Provider Authentication Policy (AP) extensions.
-
"Wicket: A simplified framework for building and testing dynamic Web pages" (Kumarsun Nadar, developerWorks, November 2008) is an overview of Wicket with a tutorial in using it to rapidly build Web-based applications.
- Learn more about Wicket, a project of the Apache Foundation.
- Brush up on DiffieHellman key exchange.
- Browse the technology bookstore for books on these and other technical topics.
- developerWorks Java technology zone: Find hundreds of articles about every aspect of Java programming.
- Get involved in the My developerWorks community.
Get products and technologies
- Download openid4java.
- Download Apache Tomcat.
- Get an OpenID from myOpenID.
Discuss
- Interested in security solutions for your Java Web applications? Check out the My developerWorks Java Security forum.

J. 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.



