(Quick Reference)

2 Usage - Reference Documentation

Authors: Burt Beckwith

Version: 1.0.4

2 Usage

The central issue with integrating OpenID logins with local application users is that OpenID providers can allow multiple login names but the provider response will always be whatever their canonical name for your identity is. For example, I can login with my Yahoo address burtbeckwith@yahoo.com but what is returned is a URL that looks like https://me.yahoo.com/a/CkkjY454mYx10td2e05dqasd5Jedt8VAgg--%27. If I had registered as a regular user on the site with burtbeckwith@yahoo.com as my username, my login would fail unexpectedly. To get it to work I'd have to know my https://me.yahoo.com/... name to propertly register, which will frustrate users.

When a user authenticates in Spring Security, an Authentication is created and stored in a ThreadLocal by the SecurityContextHolder. Typically the Authentication's principal is an instance of UserDetails (in the plugin this is implemented by the GrailsUser class) and this is a very simple class. It's really just a POJO with fields for username, password, granted authorities, and status booleans (enabled, account locked, etc.) So when an OpenID user has no associated local account, there's no direct way to specify authorities or the statuses (the password is optional in this case since that's only used for database authentication). We can assume the user is enabled as long as the OpenID authentication succeeded, so we really just need a way to determine what an OpenID user's roles are, otherwise they won't be able to do any more in the application than a non-authenticated user.

Associating OpenIDs with local accounts

This plugin has two features that address this issues. One is the ability to associate multiple OpenIDs with a user record. Recall that the Core plugin generates a user class that looks like this:

package com.yourcompany.yourapp

class User {

String username String password boolean enabled boolean accountExpired boolean accountLocked boolean passwordExpired

static constraints = { username blank: false, unique: true password blank: false }

Set<Role> getAuthorities() { UserRole.findAllByUser(this).collect { it.role } as Set } }

So to use this plugin you need to add a hasMany for a collection of OpenID domain classes (generated by the s2-create-openid script) used to store OpenIDs:

package com.yourcompany.yourapp

class User {

String username String password boolean enabled boolean accountExpired boolean accountLocked boolean passwordExpired

static hasMany = [openIds: OpenID]

static constraints = { username blank: false, unique: true password blank: false }

Set<Role> getAuthorities() { UserRole.findAllByUser(this).collect { it.role } as Set } }

Now when an existing user authenticates with OpenID, you can detect that there's no local database with that username and display a page where the user can associate that OpenID with an existing account. Subsequent authentication attempts will use the plugin's enhanced UserDetailsService that looks for a user not just by username but also by OpenID, so OpenID authenticate attempts work fine, and form-based logins do too if they provide the correct password.

New accounts

That works for existing accounts, but how do we create these in the first place? When Spring Security throws a UserNotFoundException after a successful OpenID login, the plugin detects that the authentication is a valid OpenID authentication, and if configured to do so (i.e. if grails.plugins.springsecurity.openid.registration.autocreate is true) will redirect the user to a signup page. This way you can guide them through the process of creating an account. This is more efficient than presenting a regular registration workflow because their canonical OpenID for that provider will already be known and can be associated with the user record.