logo

Hacking Custom Authentication Providers with Grails Spring Security

logo

The Grails Spring Security plugin totally rocks. Even though Peter wrote a fantastic chapter on it for Grails in Action, I’ve always been a bit scared of it (based on some early bad experiences with the raw acegi codebase which *was* pretty insanely complex to get going).

Anyways, I’ve had cause to revisit Spring Security for a new client project, and had some tricky corner cases to solve. In particular, the app exposes a REST API that needs a special custom security provider. The client is issued an API key when they purchase the COTS product, and (basically) a hash of this key is remoted by a rich client during the authentication process so they can access backend services. However they can also access parts of the app using a normal browser interface with a user password and standard “remember me” features.

So the trick was supporting both a custom auth mechanism for certain URLs (eg /api/**) whilst maintaining usernames and passwords for the rest of the app. Turns out that it’s all totally doable. First a disclaimer: I know next to nothing about Spring Security, so there is probably way better ways to accomplish this, so feel free to give feedback about how I could simplify all this so that future googlers can benefit. That aside, here’s my crack.

Ok. To get all this custom stuff happening you’ll need to implement a few things:

  • A custom Authentication object to hold the credentials you scrape from the client’s http POST
  • A custom AuthenticationProvider which checks the credentials in that Authentication object match with the ones you’ve got stored against your backend database.
  • A custom SpringSecurityFilter which slips into the request pipeline all /api/** URLs and fires when it finds some credentials to use. It will need to extract out the credentials into an Authentication object, then pass it off to the AuthenticationManager which will inturn fire you custom provider.
  • Plenty of brain space to hold all that complexity…

Let’s start with the easy stuff and define our custom Authentication object to hold our credentials. You really only need a custom one so that your AuthenticationProvider class can answer true to supportsClass(auth), and there’s probably good adaptors I could have subclassed. Given I’m doing it all in Groovy, it’s very concise to implement the entire interface anyways:

import org.springframework.security.*

class CustomAppTokenAuthentication implements Authentication {

	String name
	GrantedAuthority[] authorities
 	Object credentials
 	Object details
 	Object principal
 	boolean authenticated

}

Ok. We have our “holder” for the credentials that the user is going to present. I’m going to populate the credentials and principal details from the incoming http request. It’s the role of the SpringSecurityFilter to do that scraping, then fire off the AuthenticationManager‘s pipeline of AuthenticationProviders. Here’s rough crack at a custom Filter:

import org.springframework.security.ui.*
import org.springframework.security.context.*
import org.springframework.beans.factory.*
import org.springframework.context.*
import javax.servlet.*
import javax.servlet.http.*

class CustomAppTokenFilter extends SpringSecurityFilter implements InitializingBean{

	def authenticationManager
	def customProvider

	void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {

		if (SecurityContextHolder.getContext().getAuthentication() == null) {

			def userId = request.getParameter("userId")
			def apiKey = request.getParameter("apiKey")
			if ( userId && apiKey ) {

				def myAuth = new CustomAppTokenAuthentication(
						name: userId,
						credentials: apiKey,
						principal: userId,
						authenticated: true
				)

				myAuth = authenticationManager.authenticate(myAuth);
				if (myAuth) {
					println "Successfully Authenticated ${userId} in object ${myAuth}"

					// Store to SecurityContextHolder
					SecurityContextHolder.getContext().setAuthentication(myAuth);
				 }
			}
		}
		chain.doFilter(request, response)
	}

	int getOrder() {
		return FilterChainOrder.REMEMBER_ME_FILTER
	}

	void afterPropertiesSet() {
		def providers = authenticationManager.providers
		providers.add(customProvider)
		authenticationManager.providers = providers
	}
}

There’s a little magic going on here in afterPropertiesSet() where I add my custom AuthenticationProvider (which is going to actually validate the token) to the existing pipeline of AuthenticationManager providers. I was hoping to do that via configuration, but couldn’t find out how. Ideas?

With all that in place, the actual logic of firing the request is pretty straightforward. If there is a userId and apiKey coming in, and the user hasn’t already been authenticated, you create your custom Authentication holder object for the credentials (contrary to what you might think, the authenticated: true attribute means “I should be inspected by an AutheticationProvider classes” NOT “the user has been authenticated with this credential”). Traps for young players.

The last piece of code we’ll need is that AuthenticationProvider to actually validate the credential. That will need to look up the user’s details in the security database. You can use the plugin’s userDetailsService to do the heavy lifting of that one. Let’s you look up the user in the database based on their user id, then gives you a handle to the underlying domain class to get any attributes you need. Here’s my rough impl:

import org.springframework.security.*
import org.springframework.security.providers.*
import org.springframework.security.userdetails.*

class CustomAppTokenAuthenticationProvider implements AuthenticationProvider {

	def userDetailsService

	Authentication authenticate(Authentication customAuth) {
		def userDetails = userDetailsService.loadUserByUsername(customAuth.principal)
		if (userDetails?.domainClass?.apiKey == customAuth.credentials) {
			customAuth.authorities = userDetails.authorities
			return customAuth
		} else {
			return null
		}
	}

	boolean supports(Class authentication) {
		return CustomAppTokenAuthentication.class.isAssignableFrom(authentication)
	}

}

At this stage I’m just comparing that the cleartext API key’s match. In the next round I’ll use a Grails codec to do the actual .encodeAsCrazyHash algo that I’ll need for the real crypto.

With all the code in place, we just need the config to wire it all together. Let’s start with the spring bean definitions for /grails-app/conf/spring/resources.groovy:

import org.codehaus.groovy.grails.plugins.springsecurity.*
import au.com.bytecode.auth.*

beans = {

	if (AuthorizeTools.securityConfig.security.active) {

		customAppTokenFilter(CustomAppTokenFilter) {
			userDetailsService = ref("userDetailsService")
			authenticationManager = ref("authenticationManager")
			customProvider = ref("customAppTokenAppTokenAuthenticationProvider")
		}

		customAppTokenAppTokenAuthenticationProvider(CustomAppTokenAuthenticationProvider) {
	   		userDetailsService = ref("userDetailsService")
		}

	}

}          

I use the plugin’s AuthorizeTools class just to check that the plugin is turned on in SecurityConfig.groovy. It’s useful to be able to turn off security for some testing via that active = false flag, so I wanted to honour that setting.

The last piece of config is making sure the filter fires only for the /api/** URL Mapping. The SecurityConfig.groovy setup allow you to specify a map of URL to pipeline. I’m not sure which filters I need in addition to my custom one, so I just specify them all :-) — you need some of them to persist your custom credential to the http session, for instance. And some to handle the anonymous user who don’t submit a token (so that your standard @Secured annotations work on the target controller). Here’s the extract:

   filterInvocationDefinitionSourceMap = [
    '/api/**': 'httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,customAppTokenFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor',
    '/**': 'JOINED_FILTERS',
	]

The JOINED_FILTERS tag means “all the standard filters”, but you can’t seem to map “/api/**” to “customAppTokenFilter,JOINED_FILTERS” which could have been nice.

Phew! That was quite a journey! As I said, if you have better ways of simplifying this process, I’m all ears. At least here’s a rough outline of how to hack together a custom authentication mechanism if your client needs one!

13 Responses to “Hacking Custom Authentication Providers with Grails Spring Security”

  1. Cool stuff; this should be very helpful for people who want to do this sort of thing in the future.

    The way to add a provider by convention is to declare the ‘providerNames’ attribute in SecurityConfig.groovy. You should always include the anonymous provider, and if you want to support remember-me cookies you want to include that one too, so this should work for you:

    providerNames = ['customAppTokenAppTokenAuthenticationProvider',
    'anonymousAuthenticationProvider', 'rememberMeAuthenticationProvider']

    The plugin will populate the provider list with the beans named in the provider name list.

  2. Glen Smith says:

    Thanks mate. That was just what I was after!

  3. Felipe says:

    Hi Glen
    in your opinion, do you have a prefered Security solution to use with Grails?
    - Spring Security plugin (http://www.grails.org/plugin/acegi)
    - Shiro Plugin (http://www.grails.org/plugin/shiro)
    - Authentication Plugin (http://grails.org/plugin/authentication)
    - “home made”
    - any other?

  4. Gordon C says:

    Thanks for posting this, its very helpful. Also I noticed in your bean config you are injecting userDetailsService into your customAppTokenFilter. But the CustomAppTokenFilter has not reference to this bean. Unless im missing something.

  5. Aaron Zirbes says:

    Thanks Glen! This was very very helpful!
    I had to remove the ‘userDetailsService = ref(“userDetailsService”)’ line inside the customAppTokenFilter bean initialization. I also noticed your customAppTokenAppTokenAuthenticationProvider may have been intended to be customAppTokenAuthenticationProvider.

  6. Michael Freeman says:

    Would be great to see a version of this for the Spring Security Core Plugin.

    I can’t seem to find the SpringSecurityFilter class, I’m sure its still possible to provide your own AuthenticationProvider I just can’t figure out how to call it without the SpringSecurityFilter.

  7. Antony Jukes says:

    Hi, thanks glen.
    Im having the same problem as Michael. SpringSecurityFilter has been dropped from spring 3. I read somewhere about extending GenericFilterBean. Im going to give that a try, I’ll report back my findings

  8. Felipe says:

    Hello Glen,

    Where I need to put each groovy file? I put inside “src/groovy” folder but I’m getting erros:
    unable to resolve class AuthenticationProvider
    unable to resolve class Authentication
    And so on…

    Thank you,

    Felipe

  9. gopal says:

    Felipe

    Did you figure ? where all files will go !

  10. sophi says:

    Thanks for publishin this, I’m soo new to spring and I was kinda bumping my head to the wall till I found this fortunately, but I have some probs/ques:
    As I see there’s no SecurityConfig.groovy in my app and spring-security-core, instead everytjings configurated in config.groovy so where should I inject filterInvocationDefinitionSourceMap??
    I have done all the steps you’ve included except the ‘filterInvocationDefinitionSourceMap’ and my filter’s never used it seems, is ‘filterInvo…’ the reason or I’m missing sth else?

  11. Utsav says:

    This is Really a very helpful article…
    I am trying to implement Spring security core to my rest services.
    I have tried above mentioned code stuck with an exception

    i.e
    No AuthenticationProvider found for org.CustomAppTokenAuthenticationbut

    Line | Method
    >> 58 | doFilter in org.CustomAppTokenFilter
    – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - -
    46 | doFilterInternal in org.grails.jaxrs.web.JaxrsFilter
    885 | runTask . . . . in java.util.concurrent.ThreadPoolExecutor$Worker

    I guess i am missing something
    Can anybody help me on this?

    Thanks!
    Utsav

  12. Saravanan K says:

    Hi,

    I am trying to use spring security plugin for ‘JAAS’. I unable to find any examples or steps. Could you please help by giving some steps.

    Saravanan K

logo
logo
Powered by WordPress | Designed by Elegant Themes