Grails Custom AuthenticationProvider

In order to tighten up security in our new Grails app I went about implementing the Spring Security Plugin. Getting it up and running with a standard username/password scenario was simple, as that is all wired up automagically by the plugin. That solved half of my problem, but we also need to support authentication with SAML, and there were no clear examples of how to do that. I’d like to share what I built in case anyone has a similar requirement. I won’t focus on the SAML specifics, but rather on how to build any custom authentication provider in grails.
You can map a URL to a filter by extending AbstractAuthenticationProcessingFilter and registering it with Spring. Then you can provide that URL for custom authentication. In my case it looked something like this:
class SamlAuthenticationFilter extends AbstractAuthenticationProcessingFilter  {

    public SamlAuthenticationFilter() {
        super("/somecustomauth")
    }

    @Override
    Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod())
        }

        String accessToken = request.getParameter("sometoken")
        return this.getAuthenticationManager().authenticate(new SamlAuthenticationToken(accessToken));
    }

}

The filter is then setup as a Spring bean, along with an authentication provider which I’ll discuss shortly:

import SamlAuthenticationFilter
import SamlAuthenticationProvider

beans = {
    samlAuthenticationFilter(SamlAuthenticationFilter) {
        authenticationManager = ref('authenticationManager')
        sessionAuthenticationStrategy = ref('sessionAuthenticationStrategy')
        authenticationSuccessHandler = ref('authenticationSuccessHandler')
        authenticationFailureHandler = ref('authenticationFailureHandler')
        rememberMeServices = ref('rememberMeServices')
        authenticationDetailsSource = ref('authenticationDetailsSource')
    }

    samlAuthenticationProvider(SamlAuthenticationProvider) {
        sAMLAuthenticationService = ref('SAMLAuthenticationService')
        sAMLSettingsService = ref('SAMLSettingsService')
        userDetailsService = ref('userDetailsService')
        passwordEncoder = ref('passwordEncoder')
        userCache = ref('userCache')
        saltSource = ref('saltSource')
        preAuthenticationChecks = ref('preAuthenticationChecks')
        postAuthenticationChecks = ref('postAuthenticationChecks')
    }
}

And the bean is then registered as a filter in the Bootstrap:

import org.codehaus.groovy.grails.plugins.springsecurity.SecurityFilterPosition
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils

class BootStrap {

    def init = { servletContext ->
        SpringSecurityUtils.clientRegisterFilter('samlAuthenticationFilter', SecurityFilterPosition.SECURITY_CONTEXT_FILTER.order + 10)
    }

    def destroy = {
    }
}

We also need to create the Token class that is used by the Filter and the Authentication Provider:

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.userdetails.UserDetails

class SamlAuthenticationToken extends UsernamePasswordAuthenticationToken {

    String token

    public SamlAuthenticationToken(String token) {
        super(null, null);
        this.token = token;
    }

    public SamlAuthenticationToken(UserDetails principal, String samlResponse) {
        super(principal, samlResponse, principal.getAuthorities())
    }

}

And finally the AuthenticationProvider itself:

import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.core.Authentication
import sonicg.authentication.SAMLAuthenticationService

class SamlAuthenticationProvider extends DaoAuthenticationProvider {

    @Override
    Authentication authenticate(Authentication authentication) {
        def token = (SamlAuthenticationToken) authentication

        def user = // define user if credentials check out

        if (user){
            def userDetails = userDetailsService.loadUserByUsername(user.username)
            def token1 = new SamlAuthenticationToken(userDetails, token.samlResponse)
            return token1
        }else{
            return null
        }

    }

    @Override
    public boolean supports(Class authentication) {
        return (SamlAuthenticationToken.class.isAssignableFrom(authentication));
    }
}

The last piece of the puzzle is to tell Spring to try using this authentication provider before the other standard three in Config.groovy:

grails.plugins.springsecurity.providerNames = [
        'samlAuthenticationProvider',
        'daoAuthenticationProvider',
        'anonymousAuthenticationProvider',
        'rememberMeAuthenticationProvider']
In this case it’s important that the custom filter goes first, as it’s Token is a subclass of UsernamePasswordAuthenticationToken. If the DAO provider was first it would try to authenticate the custom token before our filter gets a chance.
That’s it! Hopefully this proves useful to someone. It’s also just a first draft, and perhaps once the security requirements evolve I can refine the implementation and share what I’ve learned.

Reference: Custom AuthenticationProvider with Grails from our JCG partner Kali Kallin at the Kallin Nagelberg’s journey into the west blog.

Related Whitepaper:

Java Essential Training

Author David Gassner explores Java SE (Standard Edition), the language used to build mobile apps for Android devices, enterprise server applications, and more!

The course demonstrates how to install both Java and the Eclipse IDE and dives into the particulars of programming. The course also explains the fundamentals of Java, from creating simple variables, assigning values, and declaring methods to working with strings, arrays, and subclasses; reading and writing to text files; and implementing object oriented programming concepts. Exercise files are included with the course.

Get it Now!  

Leave a Reply


− 2 = seven



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books
Get tutored by the Geeks! JCG Academy is a fact... Join Now
Hello. Add your message here.