Opening up to OpenID with Spring Security

Exclusive offer: get 50% off this eBook here
Spring Security 3

Spring Security 3 — Save 50%

Secure your web applications against malicious intruders with this easy to follow practical guide

$29.99    $15.00
by Peter Mularien | May 2010 | Java Open Source Web Development

OpenID is a very popular form of trusted identity management that allows users to manage their identity through a single trusted provider. This convenient feature provides users with the security of storing their password and personal information with the trusted OpenID provider, optionally disclosing this personal information upon request. Additionally, the OpenID-enabled website can have confidence that the users providing OpenID credentials is who they say they are.

In this article by Peter Mularien, author of the book Spring Security 3, we will:

  • Learn to set up our own OpenID in less than five minutes
  • Configure the JBCP Pets website with a very rapid implementation of OpenID
  • Learn the conceptual architecture of OpenID and how it provides your site with trustworthy user access
  • Implement OpenID-based user registration
  • Experiment with OpenID attribute exchange for user profile functionality

(For more resources on Spring, see here.)

The promising world of OpenID

The promise of OpenID as a technology is to allow users on the web to centralize their personal data and information with a trusted provider, and then use the trusted provider as a delegate to establish trustworthiness with other sites with whom the user wants to interact.

In concept, this type of login through a trusted third party has been in existence for a long time, in many different forms (Microsoft Passport, for example, became one of the more notable central login services on the web for some time). OpenID's distinct advantage is that the OpenID Provider needs to implement only the public OpenID protocol to be compatible with any site seeking to integrate login with OpenID. The OpenID specification itself is an open specification, which leads to the fact that there is currently a diverse population of public providers up and running the same protocol. This is an excellent recipe for healthy competition and it is good for consumer choice.

The following diagram illustrates the high-level relationship between a site integrating OpenID during the login process and OpenID providers.

We can see that the user presents his credentials in the form of a unique named identifier, typically a Uniform Resource Identifier (URI), which is assigned to the user by their OpenID provider, and is used to uniquely identify both the user and the OpenID provider. This is commonly done by either prepending a subdomain to the URI of the OpenID provider (for example, https://jamesgosling.myopenid.com/), or appending a unique identifier to the URI of the OpenID provider URI (for example, https://me.yahoo.com/jamesgosling). We can visually see from the presented URI that both methods clearly identify both the OpenID provider(via domain name) and the unique user identifier.

Don't trust OpenID unequivocally!

You can see here a fundamental assumption that can fool users of the system. It is possible for us to sign up for an OpenID, which would make it appear as though we were James Gosling, even though we obviously are not. Do not make the false assumption that just because a user has a convincing-sounding OpenID (or OpenID delegate provider) they are the authentic person, without requiring additional forms of identification. Thinking about it another way, if someone came to your door just claiming he was James Gosling, would you let him in without verifying his ID?

The OpenID-enabled application then redirects the user to the OpenID provider, at which the user presents his credentials to the provider, which is then responsible for making an access decision. Once the access decision has been made by the provider, the provider redirects the user to the originating site, which is now assured of the user's authenticity.

OpenID is much easier to understand once you have tried it. Let's add OpenID to the JBCP Pets login screen now!

Signing up for an OpenID

In order to get the full value of exercises in this section (and to be able to test login), you'll need your own OpenID from one of the many available providers, of which a partial listing is available at http://openid.net/get-an-openid/. Common OpenID providers with which you probably already have an account are Yahoo!, AOL, Flickr, or MySpace. Google's OpenID support is slightly different, as we'll see later in this article when we add Sign In with Google support to our login page. To get full value out of the exercises in this article, we recommend you have accounts with at least:

  • myOpenID
  • Google

Enabling OpenID authentication with Spring Security

Spring Security provides convenient wrappers around provider integrations that are actually developed outside the Spring ecosystem. In this vein, the openid4java project ( http://code.google.com/p/openid4java/) provides the underlying OpenID provider discovery and request/response negotiation for the Spring Security OpenID functionality.

Writing an OpenID login form

It's typically the case that a site will present both standard (username and password) and OpenID login options on a single login page, allowing the user to select from one or the other option, as we can see in the JBCP Pets target login page.

The code for the OpenID-based form is as follows:

<h1>Or, Log Into Your Account with OpenID</h1>
<p>
Please use the form below to log into your account with OpenID.
</p>
<form action="j_spring_openid_security_check" method="post">
<label for="openid_identifier">Login</label>:
<input id="openid_identifier" name="openid_identifier" size="20"
maxlength="100" type="text"/>
<img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="images/openid.png" alt="OpenID"/>
<br />
<input type="submit" value="Login"/>
</form>

The name of the form field, openid_identifier, is not a coincidence. The OpenID specification recommends that implementing websites use this name for their OpenID login field, so that user agents (browsers) have the semantic knowledge of the function of this field. There are even browser plug-ins such as Verisign's OpenID SeatBelt ( https://pip.verisignlabs.com/seatbelt.do), which take advantage of this knowledge to pre-populate your OpenID credentials into any recognizable OpenID field on a page.

You'll note that we don't offer the remember me option with OpenID login. This is due to the fact that the redirection to and from the vendor causes the remember me checkbox value to be lost, such that when the user's successfully authenticated, they no longer have the remember me option indicated. This is unfortunate, but ultimately increases the security of OpenID as a login mechanism for our site, as OpenID forces the user to establish a trust relationship through the provider with each and every login.

Configuring OpenID support in Spring Security

Turning on basic OpenID support, via the inclusion of a servlet filter and authentication provider, is as simple as adding a directive to our <http> configuration element in dogstore-security.xml as follows:/

 

<http auto-config="true" ...>
<!-- Omitting content... -->
<openid-login/>
</http>

After adding this configuration element and restarting the application, you will be able to use the OpenID login form to present an OpenID and navigate through the OpenID authentication process. When you are returned to JBCP Pets, however, you will be denied access. This is because your credentials won’t have any roles assigned to them. We’ll take care of this next.

Adding OpenID users

As we do not yet have OpenID-enabled new user registration, we'll need to manually insert the user account (that we'll be testing) into the database, by adding them to test-users-groups-data.sql in our database bootstrap code. We recommend that you use myOpenID for this step (notably, you will have trouble with Yahoo!, for reasons we'll explain in a moment). If we assume that our OpenID is https://jamesgosling.myopenid.com/, then the SQL that we'd insert in this file is as follows:

insert into users(username, password, enabled, salt) values ('https://
jamesgosling.myopenid.com/','unused',true,CAST(RAND()*1000000000 AS
varchar));
insert into group_members(group_id, username) select id,'https://
jamesgosling.myopenid.com/' from groups where group_
name='Administrators';

You'll note that this is similar to the other data that we inserted for our traditional username-and password-based admin account, with the exception that we have the value unused for the password. We do this, of course, because OpenID-based login doesn't require that our site should store a password on behalf of the user! The observant reader will note, however, that this does not allow a user to create an arbitrary username and password, and associate it with an OpenID—we describe this process briefly later in this article, and you are welcome to explore how to do this as an advanced application of this technology.

At this point, you should be able to complete a full login using OpenID. The sequence of redirects is illustrated with arrows in the following screenshot:

We've now OpenID-enabled JBCP Pets login! Feel free to test using several OpenID providers. You'll notice that, although the overall functionality is the same, the experience that the provider offers when reviewing and accepting the OpenID request differs greatly from provider to provider.

Spring Security 3 Secure your web applications against malicious intruders with this easy to follow practical guide
Published: May 2010
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

(For more resources on Spring, see here.)

The OpenID user registration problem

Try using the same technique that we worked through previously with a Yahoo! OpenID—for example, https://me.yahoo.com/pmularien . You will find that it doesn't work, as it did with the other OpenID providers. This illustrates a key problem with the structure of OpenID, and highlights the importance of OpenID-enabled user registration.

How OpenID identifiers are resolved

The actual OpenID that Yahoo! returns will be similar to the following: https://me.yahoo.com/pmularien#9a466. In OpenID terminology, the identifier that the user enters in the login box is known as the user-supplied identifier. This identifier may not actually correspond to the identifier that uniquely identifies the user (the user's claimed identifier), but as part of the verification of ownership, the OpenID Provider will take care of translating the user input to the identifier that the provider can actually prove that the user owns.

The OpenID discovery protocol and the OpenID provider itself actually have to be smart about figuring out what the user meant based on what they supply upon OpenID authentication. For example, try entering the name of an OpenID provider (for example, www.yahoo.com) in the OpenID login box—you'll get a slightly different interface that allows you to pick your OpenID, as you didn't supply a unique OpenID in the login box. Pretty clever! For details on this and other aspects of the OpenID specifications, check out the specifications page (on the developers page) of the OpenID Foundation website at http://openid.net/developers/.

Once the user is able to provide proof of ownership of their claimed identifier, the OpenID provider will return to the requesting application a normalized version of the claimed identifier, known as the OpenID Provider Local Identifier (or OP-Local Identifier). This is the final, unique identifier that the OpenID provider indicates that the user owns, and the one which will always be returned from authentication requests to the provider. Hence, this is the identifier that the JBCP Pets should be storing for user identification.

The flow of an OpenID login request handled by Spring Security proceeds as follows:

  • The o.s.s.openid.OpenIDAuthenticationFilter is responsible for listening for the pseudo-URL /j_spring_openid_security check and responding to the user's login request, much as the UsernamePasswordAuthenticationFilter does for the /j_spring_ security_check URL. We can see from the diagram that the o.s.s.openid. OpenID4JavaConsumer delegates to the openid4java library to construct the URL which ultimately redirects the user to the OpenID provider. The openid4java library (via the org.openid4java.consumer.ConsumerManager) is also responsible for the provider discovery process described earlier. This filter is actually used in both phases of OpenID authentication—both in formulating the redirect to the OpenID provider, and the handling of the authentication response from the provider. The response from the OpenID provider is a simple GET request, with a series of well-defined fields which are consumed and verified by the openid4java library. While you won't be dealing with these fields directly, some of the important ones are as follows:

Field Name

Description

openid.op_endpoint

The OpenID Provider's endpoint URL used for verification.

openid.claimed_id

The OpenID claimed identifier provided by the user.

openid.response_nonce

The nonce calculated by the provider, used to create the

signature.

openid.sig

The OpenID response signature.

openid.association

The one-time use association generated by the requestor

and is used to calculate the signature, and determine the

validity of the response.

openid.identifier

The OP-Local identifier.

We'll examine how some of these fields are used in verifying the validity of a response. Let's look at the actors involved in processing the vendor's OpenID response:

We see that the user is redirected to /j_spring_openid_security_check after they submit their credentials to the OpenID provider's site. The OpenIDAuthenticationFilter performs some rather basic checks to see if the invoking request is an OpenID request (from the JBCP Pets login form), or a possibly valid OpenID response from a provider.

Once the request is determined to be an OpenID response, a complex series of validations ensures to validate the correctness and authenticity of the response (refer to the section Is OpenID secure? later in this article for more details on this). The OpenID4JavaConsumer eventually returns a sparsely populated o.s.s.openid. OpenIDAuthenticationToken, which is used by the filter to determine whether the initial validation of the response was successful. The token is then passed along to the AuthenticationManager, which treats it like any other Authentication object.

The o.s.s.openid.OpenIDAuthenticationProvider ends up being responsible for performing final verification against the local authentication store (for example, JdbcDaoImpl). It's important to remember that what is expected in the authentication store is a username containing the OP-Local Identifier, which may not necessarily match the identifier initially supplied by the user—this is the crux of the OpenID registration problem. The flow from this point onward is very similar to traditional username/password authentication, most notably in the retrieval of appropriate group and role assignments from the UserDetailsService.

Implementing user registration with OpenID

For a user to be able to create an account on the JBCP Pets site, which will be OpenID enabled, they'll need to first prove that they own the identifier. Thus, we'll allow the user to supply an OpenID as part of the registration process. We've taken the liberty of adding a registration process for standard username and password authentication, which you can walk through using the Registration link in the header. Let's extend this registration process to allow a user to register for an account using OpenID.

Adding the OpenID registration option

Initially, we'll need to add a simple form, much like the login form (actually, exactly the same!) to the registration page, to allow the user to present and validate their OpenID identifier. Add the following to the end of registration.jsp:

<h1>Or, Register with OpenID</h1>
<p>
Please use the form below to register your account with OpenID.
</p>
<form action="j_spring_openid_security_check" method="post">
<label for="openid_identifier">Login</label>:
<input id="openid_identifier" name="openid_identifier" size="50"
maxlength="100" type="text"/>
<img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="images/openid.png" alt="OpenID"/>
<br />
<input type="submit" value="Login"/>
</form>

This form is, in fact, exactly the same as the form on the login page. How can we differentiate between a login and a registration request?

Differentiating between a login and registration request

We chose a very simple method of differentiating between a login and registration request. If the user makes a successful OpenID authentication attempt, and they haven't already got a valid account in our database, we'll assume that it's a registration request and add them to the database. There are certainly ways to refine this behavior (for example, displaying a confirmation message to the user before creating an account!), but for the purposes of our example, this is sufficient.

We'll extend the standard AuthenticationFailureHandler in com.packtpub. springsecurity.security.OpenIDAuthenticationFailureHandler class, as follows:

package com.packtpub.springsecurity.security;
// imports omitted
public class OpenIDAuthenticationFailureHandler extends
SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
if(exception instanceof UsernameNotFoundException
&& exception.getAuthentication() instanceof
OpenIDAuthenticationToken
&& ((OpenIDAuthenticationToken)exception.getAuthentication()).
getStatus().equals(OpenIDAuthenticationStatus.SUCCESS)) {
DefaultRedirectStrategy redirectStrategy = new
DefaultRedirectStrategy();
request.getSession(true).setAttribute("USER_OPENID_CREDENTIAL", ((Us
ernameNotFoundException)exception).getExtraInformation());
// redirect to create account page
redirectStrategy.sendRedirect(request, response, "/
registrationOpenid.do");

} else {
super.onAuthenticationFailure(request, response, exception);
}
}
}

We see that this code extends a sensible superclass for default behavior, redirecting the user to the registrationOpenid.do URL only if the following criteria are true:

  • The user has encountered a UsernameNotFoundException
  • The user has successfully authenticated by the OpenID provider (this is validated by checking the OpenIDAuthenticationToken's OpenIDAuthenticationStatus value.

The code sets the value of the OP-Local Identifier returned by the OpenID provider in the session, so that we can retrieve it after the redirection to the OpenID registration URL.

Configuring a custom authentication failure handler

We'll need to configure this authentication failure handler with a simple adjustment in the <openid-login> declaration in dogstore-security.xml:

<openid-login authentication-failure-handler-ref="openIdAuthFailureHa
ndler">
The corresponding bean can be declared in dogstore-base.xml:
<bean id="openIdAuthFailureHandler" class="com.packtpub.
springsecurity.security.OpenIDAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login.do"/>
</bean>

The defaultFailureUrl is the location where the user will be redirected if they encounter a true login failure like providing invalid credentials.

Adding the OpenID registration functionality to the controller

The handler for OpenID-based registration is very simple, and added to the LoginLogoutController that we already have for standard username and password registration:

@RequestMapping(method=RequestMethod.GET,value="/registrationOpenid.
do")
public String registrationOpenId(HttpServletRequest request) {
String userId = (String) request.getSession().getAttribute("USER_
OPENID_CREDENTIAL");
if(userId != null) {
userService.createUser(userId, "unused", null);
setMessage(request, "Your account has been created. Please log in
using your OpenID.");
return "redirect:login.do";
} else {
setMessage(request, "Please register using your OpenID.");
return "redirect:registration.do";
}
}

Next, we'll modify our IUserService interface and UserServiceImpl to build a simple createUser method:

@Service
public class UserServiceImpl implements IUserService {
@Autowired
CustomJdbcDaoImpl jdbcDao;
// existing code omitted
@Override
public void createUser(String username, String password, String
email) {
jdbcDao.createUser(username, password, email);
}
}

You'll note that we also changed the @Autowired annotation to explicitly refer to our CustomJdbcDaoImpl. We'll need to implement a custom createUser method in this class, as follows:

@Transactional
public void createUser(String username, String password, String email)
{
getJdbcTemplate().update("insert into users(username, password,
enabled, salt) values (?,?,true,CAST(RAND()*1000000000 AS varchar))",
username, password);
getJdbcTemplate().update("insert into group_members(group_id,
username) select id,? from groups where group_name='Users'",
username);
}

If we did not have the custom users table with the salt field, we could simply change our CustomJdbcDaoImpl to inherit from JdbcUserDetailsManager to pick up the createUser method already implemented for us:

public class CustomJdbcDaoImpl
extends JdbcUserDetailsManager
implements IChangePassword {

This would necessitate some minor changes to the createUser method of the UserServiceImpl:

@Override
public void createUser(String username, String password, String email)
{
GrantedAuthority roleUser = new GrantedAuthorityImpl("ROLE_USER");
UserDetails user = new User(username, password, true, true, true,
true, Arrays.asList(roleUser));
jdbcDao.createUser(user);
}

You can see that there are two different ways of registering users, custom and out of the box. Each method is an effective way to handle the OpenID registration problem. Feel free to experiment with the sample application and pick the method that you like best!

Once the user has been created via our IUserService functionality, the user is redirected to the home page, and can successfully log in. If we wanted to enhance the user experience here, we could make some additional code changes to retain the OpenIDAuthenticationToken past the redirect and automatically authenticate the user.

Keep in mind that OP-Local identifiers can potentially be quite long—in fact, the OpenID 2.0 specification does not supply a maximum length for an OP-Local identifier. The default Spring Security JDBC schema provides a relatively small username column (which you may recall that we already extended from the default to 100 characters). Depending on your needs, you may wish to extend the username column further to accommodate long identifiers, or subclass some portion of the OpenID handling chain (for example, the OpenIDAuthenticationProvider or the UserDetailsService) so that identifiers which are too long are handled sensibly. This may include breaking the username into multiple columns or storing a truncated URL and a hash of the full URL, to uniquely identify the user. Remember that authentication isn't an issue at this point, merely being able to correctly identify the user in the database, based on their OpenID. Some OpenID-enabled sites go one step further than this, and allow a level of indirection between the OpenID identifier and the username used for authentication (for example, allowing multiple OpenIDs to be associated with the same user account). The abstraction of the OpenID from the user's account name can be helpful for those users who have multiple OpenIDs from different providers that they may wish to use on your site—although this is somewhat contrary to the goals of OpenID, it does happen, and you need to keep it in mind when designing an OpenID-enabled site.

Part of the promise of OpenID, in addition to credential management and centralized trusted authentication, is the ability for users to manage their personal information in a single location and selectively release information to participating sites. This could provide, for example, a significantly richer registration experience. Let's see how Attribute Exchange hopes to solve this problem.

Spring Security 3 Secure your web applications against malicious intruders with this easy to follow practical guide
Published: May 2010
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

(For more resources on Spring, see here.)

Attribute Exchange

One other interesting feature of OpenID is the ability for the OpenID provider to supply (upon the user's consent) typical user registration data such as name, e-mail, and date of birth, if the OpenID-enabled website requests it. This functionality is called Attribute Exchange (AX). The following diagram illustrates how a request for attribute exchange makes it into the OpenID request:

The AX attribute values ( if supplied by the provider) are returned along with the rest of the OpenID response, and inserted into the OpenIDAuthenticationToken as a list of o.s.s.openid.OpenIDAttribute

AX attributes can be arbitrarily defined by OpenID providers, but are always uniquely defined by a URI. There has been an effort to standardize the available and common attributes into a schema of sorts. Attributes such as the following are available (the full list is available at http://www.axschema.org/types/):

Attribute name

Description

http://axschema.org/contact/email

User's e-mail address

http://axschema.org/namePerson

User's full name

The axschema.org site lists over 30 different attributes, with unique URIs and descriptions. Note that you may need to reference schema.openid.net instead of axschema.org in certain cases (we'll explain why in a short time)

Let's see how to configure attribute exchange with Spring Security!

Enabling AX in Spring Security OpenID

Enabling AX support in Spring Security OpenID is actually quite trivial, once you know the appropriate attributes to request. We can configure AX support so that an e-mail address is requested as follows:

 

<openid-login authentication-failure-handler-ref="openIdAuthFailureHa
ndler">
<attribute-exchange>
<openid-attribute name="email" type="http://schema.openid.net/
contact/email" required="true"/>
</attribute-exchange>
</openid-login>

For this example, we'd suggest that you log in with your myOpenID identity. You'll see that this time, when you are redirected to the provider, the provider informs you that additional information is being requested by the JBCP Pets site. In the following screenshot, we've actually included several more AX attributes in the request

The attributes requested, if returned by the provider, are available in the OpenIDAuthenticationToken (returned with the successful authentication request) as name-value pairs, with the names as assigned in the <openid-attribute> declarations. It's up to our site to check for this data, and then do something with it. Typically, this data could be used to pre-populate a user profile or user registration form.

For investigative purposes, you can augment the OpenIDAuthenticationFailureHandler that we wrote to include code to print the retrieved attributes to the console:

 

request.getSession(true).setAttribute("USER_OPENID_CREDENTIAL", ((User
nameNotFoundException)exception).getExtraInformation());
OpenIDAuthenticationToken openIdAuth = (OpenIDAuthenticationToken)exce
ption.getAuthentication();
for(OpenIDAttribute attr : openIdAuth.getAttributes()) {
System.out.printf("AX Attribute: %s, Type: %s, Count: %d\n", attr.
getName(), attr.getType(), attr.getCount());
for(String value : attr.getValues()) {
System.out.printf(" Value: %s\n", value);
}
}
redirectStrategy.sendRedirect(request, response, "/registrationOpenid.
do");

This will produce the following output in our example:

AX Attribute: email, Type: http://schema.openid.net/contact/email,
Count: 1
Value: peter@mularien.com
AX Attribute: birthDate, Type: http://schema.openid.net/birthDate,
Count: 1
Value: 1968-04-13
AX Attribute: namePerson, Type: http://schema.openid.net/namePerson,
Count: 1
Value: Peter Mularien
AX Attribute: nickname, Type: http://schema.openid.net/namePerson/
friendly, Count: 1
Value: pmularien
AX Attribute: country, Type: http://schema.openid.net/contact/country/
home, Count: 1
Value: US

We can see that AX data is very easily retrieved from OpenID providers, which support it and can be accessed with straightforward API calls. In typical usage scenarios, as previously discussed, AX information would be used upon registration to populate user profile or preference information, saving the user time in avoiding re-keying of information they already have in their OpenID profile.

Real-world AX support and limitations

Unfortunately, the promise of AX falls far short in reality. AX is very poorly supported by the available OpenID providers in the market, with only a handful of providers offering support (myOpenID and Google being the most prominent). Additionally, there is a lot of confusion, even among providers that do support the standard, of what attributes correspond to the data that they are willing to send. For example, to query for a user's e-mail address, the attribute name to request differs even between the two major providers who support AX!

Provider

AX attribute supported

myOpenID

http://schema.openid.net/contact/email

Google

http://axschema.org/contact/email

An alternative to AX, known as Simple Registration ( SReg) is supported by the underlying openid4java library, but is not exposed through the Spring Security OpenID layer (by the choice of the developers). This is regrettable, because SReg is actually supported by many more providers than the ones who support AX. AX was intended as a more open and flexible alternative to SReg, but lack of adoption and standardization has hampered its uptake among providers.As of the time of this writing, there is ongoing discussion on OpenID-related mailing lists as to where and how to best document standard attributes, and to allow OpenID providers to advertise which attributes they support.

An alternative to AX, known as Simple Registration ( SReg) is supported by the underlying openid4java library, but is not exposed through the Spring Security OpenID layer (by the choice of the developers). This is regrettable, because SReg is actually supported by many more providers than the ones who support AX. AX was intended as a more open and flexible alternative to SReg, but lack of adoption and standardization has hampered its uptake among providers.

Google OpenID support

Google has chosen to implement OpenID slightly differently, and doesn't assign user-friendly OpenID identifiers to users. Instead, Google expects that sites choosing to offer OpenID login through Google will use the Google canonical OpenID provider URL, and users will provide credentials directly to Google. To make this process more straightforward to users, it's common for sites to provide a Sign in with Google button, which invokes the Google OpenID provider. We can add this to the login page as follows:

<form action="j_spring_openid_security_check" method="post">
<input name="openid_identifier" size="50" maxlength="100"
type="hidden" value="https://www.google.com/accounts/o8/id"/>
<input type="submit" value="Sign in with Google"/>
</form>

We can see that the Google URL needn't be exposed to the user at all. The user is presented with the typical login screen:

After the Google sign-in process is completed, the user's unique OP-Local Identifier will be returned, and the registration / login process can proceed as with any other OpenID provider

Is OpenID secure?

As support for OpenID relies on the trustworthiness of the OpenID provider and the verifiability of the provider's response, security and authenticity are critical in order for the application to have confidence in the user's OpenID-based login.

Fortunately, the designers of the OpenID specification were very aware of this concern, and implemented a series of verification steps to prevent response forgery, replay attacks, and other types of tampering, which are explained in the following:

  • Response forgery is prevented due to the combination of a shared secret key (created by the OpenID-enabled site prior to the initial request), and a one-way hashed message signature on the response itself. A malicious user tampering with the data in any of the response fields without having access to the shared secret key and signature algorithm would generate an invalid response.
  • Replay attacks are prevented due to the inclusion of a nonce, or a one-time use, random key, that should be recorded by the OpenID-enabled site so that it cannot ever be reused. In this way, even a user attempting to re-issue the response URL themselves would be foiled because the receiving site would determine that their nonce had been previously used and would invalidate the request.

The most likely form of attack that could result in a compromised user interaction would be a man-in-the-middle attack, where a malicious user could intercept the user's interaction between their computer and the OpenID provider. A hypothetical attacker in this situation could be in a position to record the conversation between the user's browser and the OpenID provider, and record the secret key used when the request was initiated. The attacker in this case would need a very high level of sophistication and a reasonably complete implementation of the OpenID signature specification—in short, this is not likely to occur with any regularity.

Do note that although the openid4java library does support the use of persistent nonce tracking using JDBC, Spring Security OpenID does not currently expose this as a configuration parameter—thus nonces are tracked only in memory. This means that a replay attack could occur after a server restart, or in a clustered environment, where the in-memory store would not be replicated between JVMs on different servers.

Summary

In this article, we reviewed OpenID, a relatively recent technology for user authentication and credentials management. OpenID has very wide reach on the web, and has made great strides in usability and acceptance within the past year or two. Most public-facing sites on the modern web should plan on some form of OpenID support, and JBCP Pets is no exception! In this article, we:

  • Learned about the OpenID authentication mechanism, and explored its high level architecture and key terminology.
  • Implemented OpenID Login and automatic user registration with the JBCP Pets site.
  • Explored the future of OpenID profile management through the use of Attribute Exchange (AX).
  • Examined the security of OpenID login responses.

Further resources on this subject:

About the Author :


Peter Mularien

Peter Mularien is an experienced software architect and engineer, and the author of the book Spring Security 3, Packt Publishing. Peter currently works for a large financial services company and has over 12 years consulting and product experience in Java, Spring, Oracle, and many other enterprise technologies. He is also the reviewer of this book.

Books From Packt

GlassFish Security
GlassFish Security

JavaFX 1.2 Application Development Cookbook
JavaFX 1.2 Application Development Cookbook

iReport 3.7
iReport 3.7

Spring Python 1.1
Spring Python 1.1

Django 1.2 E-commerce
Django 1.2 E-commerce

Drupal E-commerce with Ubercart 2.x
Drupal E-commerce with Ubercart 2.x

Liferay Portal 6 Enterprise Intranets
Liferay Portal 6 Enterprise Intranets

Grok 1.0 Web Development
Grok 1.0 Web Development

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software