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 addressburtbeckwith@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.yourappclass 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 } }
hasMany
for a collection of OpenID domain classes (generated by the s2-create-openid script) used to store OpenIDs:package com.yourcompany.yourappclass 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 } }
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 aUserNotFoundException
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.