OpenID is an open standard for decentralized authentication. The OpenID site has plenty of detailed documentation about the protocol so I will briefly explain the basics needed to understand how to integrate OpenID authentication into your grails project.
OpenID Provider
A user registers an OpenID through an OpenID provider. This is the site that provides the identifying URL for the user and handles the authentication request from the Relying Party.
There are a number of OpenID providers out there, with new ones cropping up all the time. I use myopenid simply because it was one of the first openid providers I had heard of. Many sites also provide an OpenID when you sign up for their service. This has lead to OpenID providers that aggregate all of your other OpenIDs. Since we want to drive adoption and there are probably more providers than consumers at this point, we are going to focus on just being an OpenID relying party.
Relying Party
The relying party is the web service that uses OpenID to handle it’s authentication. It prompts the user for their OpenID URL and redirects the user’s browser to the Provider’s authentication page, the provider prompts the user with information about the site making the authentication request, allows the user to accept or reject authentication with the site and then redirects the user back to the relying party with an authentication token that the relying party then verifies. The user never enters a password with the relying party, the provider can also allow automatic authentication with the relying party if they so choose.
Discovery
You can use any web address as your OpenID simply by including the following html in the head tag of your site.
<link rel="openid.server" href="http://youropenidserverendpoint"> <link rel="openid.delegate" href="http://youropenid/url">
This lets you use your blog and any other site you may have as your OpenID. The relying party will parse this information from whatever URL you pass to it.
Setup
I am using code and comments taken from the OpenID4Java getting started page and adapting it for quick integration with grails. If you need anymore detail than I have provided you should check out their wiki. The full source code for our example can be found here: UserController.groovy
1. Sign up for an OpenID, we want one to ease testing obviously.
2. Download the excellent OpenID java library OpenID4java and put the jar and its dependencies in your lib directory. Watch out for dependency versioning conflicts since grails uses many of the same dependencies.
3. For our example we will use a User controller, so go ahead and create a user controller. I won’t really go into modelling a User domain class but with OpenID you can skip all of the password management and storage and simply have a field for their OpenID URL instead.
Getting Started
We need to specify a return URL that the provider will redirect to after authentication is finished. You can store this in a number of places but I will just put it in the UserController. This URL corresponds to the auth action will will implement in UserController.
String _returnURL = "http://localhost:8080/openidtest/user/auth";
The controller actions share a ConsumerManager that is used to perform various steps of the OpenID authentication, I made it a static variable in the UserController because it seems to not work otherwise, there is probably a better way to do this.
static ConsumerManager manager = new ConsumerManager();
Actions
We will define a few actions on our controller, these will be used to initiate and verify the OpenID authentication process.
login
The first action is login, this action simply presents the user with an OpenID entry field.
def login = {
if(session.user){
redirect(action: 'index') //redirect to main user page
}
}
If the user is already logged in we redirect.
Here is gsp code for including an OpenID login field for your app, include it in your login.gsp view. Don’t forget to save the OpenID icon to your images directory.
<img src="${createLinkTo(dir:'images',file:'icon_openid.gif')}" alt="openid_logo" />
<g:form name="loginForm" action="handleLogin"><g:textField name="openid" value="http://yourname.myopenid.com" />
<g:actionSubmit value="Login" action="handleLogin" />
</g:form>
handleLogin
This action will take the user’s OpenID URL, create an authentication request and redirect the user to their OpenID Provider.
def handleLogin = {
try{
// disable realm verification
RealmVerifier rv = new RealmVerifier();
rv.setEnforceRpId(false);
manager.setRealmVerifier(rv)
// perform discovery on the user-supplied identifier
List discoveries = manager.discover(params['openid']);
DiscoveryInformation discovered = manager.associate(discoveries);
// store the discovery information in the user's session for later use
session.discovered = discovered
// obtain a AuthRequest message to be sent to the OpenID provider
AuthRequest authReq = manager.authenticate(discovered, _returnURL);
response.sendRedirect authReq.getDestinationUrl(true)
}catch(DiscoveryException e){
//add flash message , failed to find openid at address
flash.message = "Failed to find valid openid URI at specified address"
redirect(action:'login')
}
}
auth
The auth action is the action specified by our service that the OpenID provider will redirect the user to after authenticating with the provider. The provider includes a number of parameters so that the relying party can verify that the user has actually authenticated with the provider.
def auth = {
// check if this is an openid message, this is to avoid someone
// calling the auth action themselves
if(!params['openid.mode']){
redirect(action:'err')
return;
}
ParameterList openidResp = new ParameterList(request.getParameterMap());
// retrieve the previously stored discovery information
DiscoveryInformation discovered = (DiscoveryInformation) session.discovered;
// extract the receiving URL from the HTTP request
// is there an easier way to get the full path including servername and port?
StringBuffer receivingURL = new StringBuffer('http://' + request.getServerName() + ':' +
request.getServerPort() +request.forwardURI);
String queryString = request.getQueryString();
if (queryString != null && queryString.length() != 0)
receivingURL.append('?').append(request.getQueryString());
// verify the response
VerificationResult verification = manager.verify(receivingURL.toString(), openidResp, discovered);
// examine the verification result and extract the verified identifier
Identifier verified = verification.getVerifiedId();
if (verified != null){
//User.findByUrl(verfied.getIdentifier())
session.user = verified
// success, use the verified identifier to identify the user
redirect(action: 'index')
}else{
// OpenID authentication failed
redirect(action: 'err')
}
}
request.forwardURI is a grails specific method that actually calls request.request.requestURI, this is needed to verify the previously called URL that made the authentication call to the provider.
err
The err action is the action we will redirect to if our authentication fails at any point. You can probably get away with just redirecting the the grails default error page.
Filter
Our final step is to set up an Authentication filter like the one we made for basic authentication . The only difference is that we have a number of controller actions that we want to allow to pass the filter, like login, handleLogin, auth, and err.
class SecurityFilters {
class SecurityFilters {
def filters = {
loginCheck(controller:'*', action:'*') {
before = {
log.trace("inside of filter")
if(!session.user && !actionName.equals('login') &amp;&amp; !actionName.equals('handleLogin') && !actionName.equals('auth') && !actionName.equals('err')) {
redirect(controller:'user' , action:'login')
return false
}
}
}
}
}
OpenID, Authorization, and APIs
Since OpenID focuses on authentication there is not currently a good way to incorporate OpenID with API calls. Having to redirect a user to a web page is not something someone programming a mobile or desktop application wants to do in order to make API calls. There have been rumblings of integrating an API http header authorization mechanism into the OpenID standard, and of course you could always integrate OAuth and OpenID so the user would only have to do web based authorization once for your application.
Posted on March 20th, 2008 | Filed under grails, identity, java | No Comments »



