Take me home

OpenID authentication with attributes in Play

Written by August Lilleaas, published October 16, 2011

In an effort to procrastinate and still get things done, I started work on a new project even though Buster.JS isn't done yet. That's more useful than spending all day playing Minecraft, I guess.

The app will support OpenID authentication. I'm using the Play framework, which has built in support for OpenID. The docs for Attribute Exchange (AX) and Simple Registration (SREG) in Play's OpenID module are very sparse, though. So I decided to write about my discoveries here.

Here's a code-dump. The routes:

GET     /login                                  Authentication.index
POST    /login                                  Authentication.create

The view:

#{form @Authentication.create()}
  <input type="text" name="openid_url" />
  <input type="submit" value="Log in" />
#{/form}

My controller:

package controllers;

import play.mvc.Controller;
import play.libs.OpenID;
import play.libs.OpenID.*;

public class Authentication extends Controller {
    public static void index() {
        if (OpenID.isAuthenticationResponse()) {
            // Postback from OpenID provider.
            UserInfo verifiedUserInfo = OpenID.getVerifiedID();

            if (verifiedUserInfo != null) {
                // Store verifiedUserInfo.id
                if (verifiedUserInfo.extensions != null) {
                    renderText(verifiedUserInfo.extensions.toString());
                } else {
                    renderText("Logged in. Failed to get attributes");
                }
            } else {
                renderText("Failed to log in.");
            }
        } else {
            // Render login form.
            render();
        }
    }

    public static void create() {
        String openIdURL = params.get("openid_url");
        OpenID openIdReq = OpenID.id(openIdURL);
            
        // Attribute Exchange (AX)
        openIdReq.required("email", "http://axschema.org/contact/email");
        openIdReq.required("firstName", "http://axschema.org/namePerson/first");
        openIdReq.required("lastName", "http://axschema.org/namePerson/last");

        // Simple Registration (SREG)
        openIdReq.required("email");
        openIdReq.optional("fullname");
        openIdReq.optional("timezone");

        // Perform redirect
        openIdReq.verify();
    }
}

The code pretty much speaks for itself. A few things to note:

  • The AX and SREG attributes should both be in place. Different OpenID providers use different methods of attribute exchange. MyOpenId uses SREG. Google uses AX. You may not get all the attributes you request.
  • The value of the first argument for AX is optional. It specifies the key name in the user info returned from the OpenID request.
  • The value of the argument for sreg is not optional. It has to match the SREG field name conventions.
  • Attribute exchange is a mess. I'm yet to find a reference. Seems your best bet is provider specific documentation, and manual testing. Bot for SREG and AX. Here's Google's documentation, for example.
  • Google won't give you first and last name if they're optional, just to spice things up.
  • You want to store the verifiedUserInfo.id with your user record. This is what you will use to identify a future OpenID log in from the same Open ID with your internal user record.
  • The OpenID login URL for Google is https://www.google.com/accounts/o8/id. It's the same for all Google accounts, since Google will use cookies and not the URL itself to identify.

Questions or comments?

Feel free to contact me on Twitter, @augustl, or e-mail me at august@augustl.com.