3 Tutorials - Reference Documentation
Authors: Burt Beckwith
Version: 2.0-RC2
3 Tutorials
3.1 User registration and linking
In this tutorial we'll cover- creating a sample application
- installing the plugin
- configuring the plugin
- using the account linking workflow
- using the registration workflow
$ grails create-app openidtest
$ cd openidtest
Install the OpenID plugin by adding a dependency in BuildConfig.groovy:plugins { … runtime ':spring-security-openid:2.0-RC1' }
s2-quickstart script
to initialize the core plugin:
$ grails s2-quickstart com.openidtest User Role
To support the remember-me checkbox, run the s2-create-persistent-token
script to generate a domain class for persistent tokens:
$ grails s2-create-persistent-token com.openidtest.PersistentLogin
To support linking one or more OpenIDs with local accounts, we need to create an OpenID
domain class:
$ grails s2-create-openid com.openidtest.OpenID
and edit the generated user class and add a hasMany
for an openIds
property:package com.openidtestclass User { transient springSecurityService String username String password boolean enabled = true boolean accountExpired boolean accountLocked boolean passwordExpired static hasMany = [openIds: OpenID] static transients = ['springSecurityService'] static constraints = { username blank: false, unique: true password blank: false } static mapping = { password column: '`password`' } Set<Role> getAuthorities() { UserRole.findAllByUser(this).collect { it.role } as Set } def beforeInsert() { encodePassword() } def beforeUpdate() { if (isDirty('password')) { encodePassword() } } protected void encodePassword() { password = springSecurityService.encodePassword(password) } }
grails-app/conf/BootStrap.groovy
:import com.openidtest.Role import com.openidtest.User import com.openidtest.UserRoleclass BootStrap { def init = { servletContext -> def roleAdmin = new Role(authority: 'ROLE_ADMIN').save() def roleUser = new Role(authority: 'ROLE_USER').save() def user = new User(username: 'user', password: 'password', enabled: true).save() def admin = new User(username: 'admin', password: 'password', enabled: true).save() UserRole.create user, roleUser UserRole.create admin, roleUser UserRole.create admin, roleAdmin, true } }
OpenIdController
but it'll be more natural to access its createAccount
action under /login/ and we also want to use this controller's auth
action instead of the core plugin's LoginController.auth
since this one supports both OpenID and regular username/password logins, so add mappings in grails-app/conf/UrlMappings.groovy
to support these changes:class UrlMappings { static mappings = { "/login/auth" { controller = 'openId' action = 'auth' } "/login/openIdCreateAccount" { controller = 'openId' action = 'createAccount' } ... } }
$ grails create-controller secure
and add this code:package openidtestimport grails.plugin.springsecurity.annotation.Securedclass SecureController { @Secured(['ROLE_ADMIN']) def admins() { render 'Logged in with ROLE_ADMIN' } @Secured(['ROLE_USER']) def users() { render 'Logged in with ROLE_USER' } }
$ grails run-app
Navigate to http://localhost:8080/openidtest/secure/admins and you should be prompted with the login screen. Leave the Use OpenID checkbox checked and enter a valid OpenID. Don't check the remember-me checkbox yet (it doesn't work with the extended workflows where you create a new user or link an OpenID) and click the "Log in" button.After authenticating at the OpenID provider, you'll be redirected to the registration page. Note that there's a link to just associate the current OpenID with a local account - for now click the "link this OpenID" link.At the next screen enter the username and password for the user we created in BootStrap with ROLE_ADMIN ('admin'/'password') and you will be redirected to http://localhost:8080/openidtest/secure/adminsTo test that the OpenID is linked to your account, logout by navigating to http://localhost:8080/openidtest/logout/ and navigate to http://localhost:8080/openidtest/secure/admins. Logging out removes the remember-me cookie, so authenticate again with your OpenID (this time check the remember-me checkbox) and it should skip the register/link step and go directly to the secured page. You can also repeat the process and switch to the username/password login and use that.To test remember-me, close the browser and re-open it, and navigate to http://localhost:8080/openidtest/secure/admins. It should skip the authentication step entirely and show the page.User Registration
Now let's create a new user associated with an OpenID. Logout again and navigate to http://localhost:8080/openidtest/secure/users to initiate a login for a resource that requires ROLE_USER.Since you've already associated the previous OpenID with a user, you either need to use a second OpenID, or restart the application to clear out the in-memory database.Login using either the new OpenID or the original after restarting, and after authenticating externally you'll be redirected to the registration page. This time create a new user. By default the password validation rules are rather strict and you can change them, but for now enter a password that's at least 8 characters and contains at least one number and at least one symbol (e.g. !, #, $, %, etc.)You should be redirected to http://localhost:8080/openidtest/secure/users after creating the account. Now logout and log back in, both with the username/password for the account you created and the OpenID you linked and both should work.3.2 Plain OpenID
In the previous tutorial we went through two workflows to allow linking OpenIDs to local accounts. Another option is to only support OpenID logins but not associate them with local accounts. This might be useful if you want to allow people to perform some basic actions like clicking Like and Don't Like buttons, adding comments, etc.To support this, rather than showing the registration page when an authenticated OpenID user is redirected to your controller, you could just create anAuthentication
for them with dummy information. Recall that the minimum requirements to populate a UserDetails
instance are the username, the status booleans (enabled, locked out, etc.) and one or more granted authorities. You could emulate a basic application user by using their OpenID as the username, setting all statuses to true
, and granting them a virtual role, e.g. ROLE_OPENID
.Copy the plugin's grails.plugin.springsecurity.openid.OpenIdController.groovy
to your application's grails-app/controllers directory and replace the existing createAccount
action with this:def createAccount() { def config = SpringSecurityUtils.securityConfig String openId = session[OpenIdAuthenticationFailureHandler.LAST_OPENID_USERNAME] if (!openId) { flash.error = 'Sorry, an OpenID was not found' redirect uri: config.failureHandler.defaultFailureUrl return } def user = new GrailsUser(openId, 'password', true, true, true, true, [new SimpleGrantedAuthority('ROLE_OPENID')], 0) SCH.context.authentication = new UsernamePasswordAuthenticationToken( user, 'password', user.authorities) session.removeAttribute OpenIdAuthenticationFailureHandler.LAST_OPENID_USERNAME session.removeAttribute OpenIdAuthenticationFailureHandler.LAST_OPENID_ATTRIBUTES def savedRequest = requestCache.getRequest(request, response) if (savedRequest && !config.successHandler.alwaysUseDefault) { redirect url: savedRequest.redirectUrl } else { redirect uri: config.successHandler.defaultTargetUrl } }
import grails.plugin.springsecurity.userdetails.GrailsUser import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.core.context.SecurityContextHolder as SCH
ROLE_OPENID
role:package openidtestimport grails.plugin.springsecurity.annotation.Securedclass SecureController { @Secured(['ROLE_ADMIN']) def admins() { render 'Logged in with ROLE_ADMIN' } @Secured(['ROLE_USER']) def users() { render 'Logged in with ROLE_USER' } @Secured(['ROLE_OPENID']) def openid() { render 'Logged in with ROLE_OPENID' } }
grails run-app
and navigate to http://localhost:8080/openidtest/secure/openid, and login using any OpenID. Once you authenticate and get redirected back to your application you should see the text Logged in with ROLE_OPENID
indicating that you're logged in as a basic OpenID user.Note that since this is a fake role, there's no need to store it in the database since real application users will never be granted ROLE_OPENID.