Fork me on Github

Grails Spring Security Core Plugin Custom Authentication

Shows how to create a custom authentication with Spring Security Core Plugin

Authors: Sergio del Amo

Grails Version: 3.3.0

1 Grails Training

Grails Training - Developed and delivered by the folks who created and actively maintain the Grails framework!.

2 Getting Started

In this guide, you are going to write a custom authentication mechanism. Spring Security Core Plugin allows for a significant degree of customization which we are going to explore next.

2.1 What you will need

To complete this guide, you will need the following:

  • Some time on your hands

  • A decent text editor or IDE

  • JDK 1.7 or greater installed with JAVA_HOME configured appropriately

2.2 How to complete the guide

To get started do the following:


The Grails guides repositories contain two folders:

  • initial Initial project. Often a simple Grails app with additional some code to give you a head-start.

  • complete A completed example. It is the result of working through the steps presented by the guide and applying those changes to the initial folder.

To complete the guide, go to the initial folder

  • cd into grails-guides/grails-spring-security-core-plugin-custom-authentication/initial

and follow the instructions in the next sections.

You can go right to the completed example if you cd into grails-guides/grails-spring-security-core-plugin-custom-authentication/complete

3 Writing the Application

When we talk about two-factor authentication we often think about two things:

  • Something the user knows (e.g. username/password)

  • Something the user has.

For the latter, in Spain, banks often issue a Coordinate Card

caja espagna coordenadas

Users need to enter username, password and a coordinate to login into their bank.

We are going to customize Spring Security Core Plugin to achieve such a login in a Grails 3 application.

First, We need to add Spring Security Core Plugin as a dependency:

compile 'org.grails.plugins:spring-security-core:3.2.0.M1'

3.1 Domain Classes

Use s2-quickstart to generate the default Spring Security Core domain classes:

grails s2-quickstart demo User Role

s2-quickstart script generates three domain classes; User, Role and UserRole. It is recommended to move the password encoding logic outside of the domain class.

Service injection in GORM entities is disabled by default since Grails 3.2.8.

Each user is going to have a coordinate card. Thus we have modified the User domain class slightly:

package demo

import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString

@ToString(includes='username', includeNames=true, includePackage=false)
class User implements Serializable {

        private static final long serialVersionUID = 1

        transient springSecurityService

        String username
        String password
        boolean enabled = true
        boolean accountExpired
        boolean accountLocked
        boolean passwordExpired
        static hasMany = [coordinates: SecurityCoordinate]

        Set<Role> getAuthorities() {

        def beforeInsert() {

        def beforeUpdate() {
                if (isDirty('password')) {

        protected void encodePassword() {
                password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password

        static transients = ['springSecurityService']

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

        static mapping = {
                password column: '`password`'
                autowire true
package demo

import groovy.transform.CompileStatic

class SecurityCoordinate {
    String position
    String value
    static belongsTo = [user: User]

3.2 Secured Controller

This controller is restricted to users with role ROLE_CLIENT

package demo

import grails.plugin.springsecurity.annotation.Secured

class BankController {

    def index() {
        render 'Welcome to your bank'

3.3 Seed Data

We are going to populate our database with some seed data:

package demo

import grails.compiler.GrailsCompileStatic

class BootStrap {

    static Map<String, String> BANKCARD =
            ['A1': '10', 'A2': '84', 'A3': '93', 'A4': '12', 'A5': '92',
             'A6': '58', 'A7': '38', 'A8': '28', 'A9': '36', 'A10': '02',
             'B1': '99', 'B2': '29', 'B3': '10', 'B4': '23', 'B5': '33',
             'B6': '47', 'B7': '58', 'B8': '39', 'B9': '34', 'B10': '18',
             'C1': '28', 'C2': '05', 'C3': '29', 'C4': '03', 'C5': '94',
             'C6': '14', 'C7': '41', 'C8': '33', 'C9': '11', 'C10': '39',
             'D1': '01', 'D2': '49', 'D3': '39', 'D4': '79', 'D5': '53',
             'D6': '38', 'D7': '17', 'D8': '88', 'D9': '70', 'D10': '12'

    def init = { servletContext ->
        def authorities = ['ROLE_CLIENT']
        authorities.each {
            if ( !Role.findByAuthority(it) ) {
                new Role(authority: it).save()
        if ( !User.findByUsername('sherlock') ) {
            def u = new User(username: 'sherlock', password: 'elementary')
            BANKCARD.each { k, v ->
                u.addToCoordinates(new SecurityCoordinate(position: k, value: v, user: u))
            def ur = new UserRole(user: u, role:  Role.findByAuthority('ROLE_CLIENT'))

    def destroy = {

3.4 Custom Login Form

We override, both LoginController and auth.gsp, to display a random coordinate field each time the user is directed to the login form.

package demo

import grails.config.Config

class LoginController extends grails.plugin.springsecurity.LoginController implements GrailsConfigurationAware {

    List<String> coordinatePositions

    def auth() {

        def conf = getConf()

        if (springSecurityService.isLoggedIn()) {
            redirect uri: conf.successHandler.defaultTargetUrl

        def position = coordinatePositions.first()

        String postUrl = request.contextPath + conf.apf.filterProcessesUrl
        render view: 'auth', model: [postUrl: postUrl,
                                     rememberMeParameter: conf.rememberMe.parameter,
                                     usernameParameter: conf.apf.usernameParameter,
                                     passwordParameter: conf.apf.passwordParameter,
                                     gspLayout: conf.gsp.layoutAuth,
                                     position: position]

    void setConfiguration(Config co) {
        coordinatePositions = co.getProperty('security.coordinate.positions', List, []) as List<String>


We have a list of valid positions as a configuration list in application.yml

            - A1
            - A2
            - A3
            - A4
            - A5
            - A6
            - A7
            - A8
            - A9
            - A10
            - B1
            - B2
            - B3
            - B4
            - B5
            - A6
            - A7
            - B8
            - B9
            - B10
            - C1
            - C2
            - C3
            - C4
            - C5
            - C6
            - C7
            - C8
            - C9
            - C10
            - D1
            - D2
            - D3
            - D4
            - D5
            - D6
            - D7
            - D8
            - D9
            - D10

The overriden GSP file displays the coordinate position input field

    <meta name="layout" content="${gspLayout ?: 'main'}"/>
    <title><g:message code='springSecurity.login.title'/></title>
    <style type="text/css" media="screen">
    #login {
        margin: 15px 0px;
        padding: 0px;
        text-align: center;

    #login .inner {
        width: 340px;
        padding-bottom: 6px;
        margin: 60px auto;
        text-align: left;
        border: 1px solid #aab;
        background-color: #f0f0fa;
        -moz-box-shadow: 2px 2px 2px #eee;
        -webkit-box-shadow: 2px 2px 2px #eee;
        -khtml-box-shadow: 2px 2px 2px #eee;
        box-shadow: 2px 2px 2px #eee;

    #login .inner .fheader {
        padding: 18px 26px 14px 26px;
        background-color: #f7f7ff;
        margin: 0px 0 14px 0;
        color: #2e3741;
        font-size: 18px;
        font-weight: bold;

    #login .inner .cssform p {
        clear: left;
        margin: 0;
        padding: 4px 0 3px 0;
        padding-left: 105px;
        margin-bottom: 20px;
        height: 1%;

    #login .inner .cssform input[type="text"] {
        width: 120px;

    #login .inner .cssform label {
        font-weight: bold;
        float: left;
        text-align: right;
        margin-left: -105px;
        width: 110px;
        padding-top: 3px;
        padding-right: 10px;

    #login #remember_me_holder {
        padding-left: 120px;

    #login #submit {
        margin-left: 15px;

    #login #remember_me_holder label {
        float: none;
        margin-left: 0;
        text-align: left;
        width: 200px

    #login .inner .login_message {
        padding: 6px 25px 20px 25px;
        color: #c33;

    #login .inner .text_ {
        width: 120px;

    #login .inner .chk {
        height: 12px;

<div id="login">
    <div class="inner">
        <div class="fheader"><g:message code='springSecurity.login.header'/></div>

        <g:if test='${flash.message}'>
            <div class="login_message">${flash.message}</div>

        <form action="${postUrl ?: '/login/authenticate'}" method="POST" id="loginForm" class="cssform" autocomplete="off">
                <label for="username"><g:message code='springSecurity.login.username.label'/>:</label>
                <input type="text" class="text_" name="${usernameParameter ?: 'username'}" id="username"/>

                <label for="password"><g:message code='springSecurity.login.password.label'/>:</label>
                <input type="password" class="text_" name="${passwordParameter ?: 'password'}" id="password"/>

                <label for="coordinateValue">${position}</label>
                <input type="hidden" name="coordinatePosition" id="coordinatePosition" value="${position}"/>
                <input type="text" class="text_" name="coordinateValue" id="coordinateValue"/>

            <p id="remember_me_holder">
                <input type="checkbox" class="chk" name="${rememberMeParameter ?: 'remember-me'}" id="remember_me" <g:if test='${hasCookie}'>checked="checked"</g:if>/>
                <label for="remember_me"><g:message code=''/></label>

                <input type="submit" id="submit" value="${message(code: 'springSecurity.login.button')}"/>
    (function() {
        document.forms['loginForm'].elements['${usernameParameter ?: 'username'}'].focus();

3.5 Authentication Provider

Create a Custom AuthenticationProvider implementation. That it is a class which implements:

You will normally:

  • Either extend one that it is similar. For example, DaoAuthenticationProvider which comes with the plugin.

  • Or directly implement the AuthenticationProvider interface

In this guide, we choose the first option.

However, Let’s start with the artifact we are going to use to validate the coordinate supplied by the user. In this guide, we use a Grails Service.

package demo

import groovy.transform.CompileStatic

interface CoordinateValidator {

    boolean isValidValueForPositionAndUserName(String value, String position, String username)
package demo

import grails.transaction.Transactional
import groovy.transform.CompileStatic

class CoordinateValidatorService implements CoordinateValidator {

    @Transactional(readOnly = true)
    boolean isValidValueForPositionAndUserName(String v, String p, String name) {
        SecurityCoordinate.where {
            position == p && value == v && user.username == name
        }.count() as boolean

We are going to inject this service as a bean in our custom authentication provider. In order to do that, we register the bean first


We extend the DaoAuthenticationProvider to add our extra check.

package demo

import groovy.transform.CompileStatic

class TwoFactorAuthenticationProvider extends DaoAuthenticationProvider {

    CoordinateValidator coordinateValidator

    protected void additionalAuthenticationChecks(UserDetails userDetails,
                                                  UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {

        super.additionalAuthenticationChecks(userDetails, authentication)

        Object details = authentication.details

        if ( !(details instanceof TwoFactorAuthenticationDetails) ) {
            logger.debug("Authentication failed: authenticationToken principal is not a TwoFactorPrincipal");
            throw new BadCredentialsException(messages.getMessage(
                    "Bad credentials"));
        def twoFactorAuthenticationDetails = details as TwoFactorAuthenticationDetails

        if ( !coordinateValidator.isValidValueForPositionAndUserName(twoFactorAuthenticationDetails.coordinateValue, twoFactorAuthenticationDetails.coordinatePosition, ) {
            logger.debug("Authentication failed: coordiante note valid");
            throw new BadCredentialsException(messages.getMessage(
                    "Bad credentials"));


We need to register our custom AuthenticationProvider as a bean.

twoFactorAuthenticationProvider(TwoFactorAuthenticationProvider) {
    coordinateValidator = ref('coordinateValidator')
    userDetailsService = ref('userDetailsService')
    passwordEncoder = ref('passwordEncoder')
    userCache = ref('userCache')
    saltSource = ref('saltSource')
    preAuthenticationChecks = ref('preAuthenticationChecks')
    postAuthenticationChecks = ref('postAuthenticationChecks')
    authoritiesMapper = ref('authoritiesMapper')
    hideUserNotFoundExceptions = true

Moreover, we want to use our TwoFactorAuthenticationProvider instead of DaoAuthenticationProvider. In order to do that, we define the providers which should be use to authenticate:

grails.plugin.springsecurity.providerNames = [

3.6 Authentication

In order to create a custom authentication, we often need a custom Authentication.

Authentication represents the token for an authentication request or an authenticated principal once the request has been processed by the method:


This generally means:

  • Either extend an existing implementation.

  • Or directly implement the interface Authentication

What we are trying to achieve is an extension of the common username/password functionality offered already by the plugin. We don’t need a custom authentication object. Instead, we are going to use the UsernamePasswordAuthenticationToken object and place in the details property our custom information; the coordinate.

3.7 Filter

If you are using a custom Authentication, you may need to create a filter.

That means coding a custom java.servlet.Filter implementation.

You have several options:

  • Extend GenericFilterBean


  • Extend a similar filter e.g. UsernamePasswordAuthenticationFilter

  • Directly implement the interface

For this guide, we don’t need to create a custom filter. Instead, we are using the UsernamePasswordAuthenticationFilter and overriding the authenticationDetailsSource bean used by that filter.

package demo

import groovy.transform.CompileStatic

import javax.servlet.http.HttpServletRequest

class TwoFactorAuthenticationDetailsSource extends WebAuthenticationDetailsSource {

    WebAuthenticationDetails buildDetails(HttpServletRequest context) {
        def details = new TwoFactorAuthenticationDetails(context)

        String position = obtainCoordinatePosition(context)
        details.coordinatePosition = position

        String value = obtainCoordinateValue(context)
        details.coordinateValue = value

     * Get the Coordinate Position from the request.
     * @param request
     * @return
    private static String obtainCoordinatePosition(HttpServletRequest request) {
        return request.getParameter('coordinatePosition')

     * Get the Coordinate Value from the request.
     * @param request
     * @return
    private static String obtainCoordinateValue(HttpServletRequest request) {
        return request.getParameter('coordinateValue')
package demo

import groovy.transform.Canonical
import groovy.transform.CompileStatic

import javax.servlet.http.HttpServletRequest

class TwoFactorAuthenticationDetails extends WebAuthenticationDetails {
    String coordinatePosition
    String coordinateValue

    TwoFactorAuthenticationDetails(HttpServletRequest request) {

3.8 Functional Test

Grails integrate seamlessly with Geb. Thus it is easy to develop a functional test for our custom login form.

package demo

import geb.spock.GebSpec
import grails.testing.mixin.integration.Integration

class BankControllerSpec extends GebSpec {

    def 'test bank controller is secured'() {
        baseUrl = "http://localhost:${serverPort}/"
        go 'bank'

        at LoginPage

        login('sherlock', 'elementary', BootStrap.BANKCARD[position()])

        driver.pageSource.contains('Welcome to your bank')
package demo

import geb.Page

class LoginPage extends Page {

    static url = 'login/auth'

    static at = { title == 'Login' }

    static content = {
        usernameField { $('#username', 0) }
        passwordField { $('#password', 0) }
        positionField { $('#coordinatePosition', 0)}
        valueField { $('#coordinateValue', 0) }
        submitField { $('#submit', 0) }

    String position() {

    void login(String username, String password, String value) {
        usernameField << username
        passwordField << password
        valueField << value

4 Running the Application

To run the tests:

grails> test-app
grails> open test-report

5 Do you need help with Grails?

OCI sponsored the creation of this Guide. OCI offers several Grails services:

Free consultation

The OCI Grails Team includes Grails co-founders, Jeff Scott Brown and Graeme Rocher. Check our Grails courses and learn from the engineers who developed, matured and maintain Grails.

Grails OCI Team