How can I customize exception mappings of default AuthenticationEventPublisher in Grails Spring Security Plugin

148 views Asked by At

Background:

In my project, I needed to have two factor authentication (by sending OTP to registered email) which I implemented by extending DaoAuthenticationProvider.

In order to identify reason of why OTP authentication has failed, my custom authentication provider throws new custom exceptions for example BadOtpException, ExpiredOtpException, ConsumedOtpException. All these exceptions are subclasses of BadCredentialsException. This authentication flow is working fine.

Issue:

The Authentication events that were earlier getting published while I was using DaoAuthenticationProvider are now not getting published with my custom authentication provider.

Cause:

Upon some troubleshooting I figured out that Grails Spring Security Core plugin uses DefaultAuthenticationEventPublisher to publish the events. And this class publishes events on the basis of exception mappings which contain exception name vs event name to resolve the event that needs to be published whenever exception occurs. And these mappings are configured in its constructor. Since mappings of my custom exceptions are not present in this constructor, the authentication failure events are not getting published.

Tried Solution:

I tried overriding DefaultAuthenticationEventPublisher and added new exception mappings by invoking super.setAdditionalExceptionMappings(). Here is the custom event publisher:

class CustomAuthenticationEventPublisher extends DefaultAuthenticationEventPublisher {
CustomAuthenticationEventPublisher() {

}

CustomAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
    super(applicationEventPublisher)
    println('CustomAuthenticationEventPublisher')
    Properties exceptionMappings = new Properties()
    exceptionMappings.setProperty(BadOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
    super.setAdditionalExceptionMappings(exceptionMappings)
}
}

In resources.groovy, I registered my custom event publisher using following:

beans = {
   authenticationEventPublisher(CustomAuthenticationEventPublisher)
}

But above solution is not working. Even the println() statement in the constructor is not getting logged. Seems like the bean is not getting registered.

Am I doing anything wrong in the above solution?
Is there any other way I can override the exception mappings?

1

There are 1 answers

0
Manish Kapoor On

With bit of more troubleshooting, I realized that zero argument constructor of the class CustomAuthenticationEventPublisher was being called instead of the other one.
So I tried setting the exception mappings in constructor and it worked. Here is the code that worked form me:

class CustomAuthenticationEventPublisher extends DefaultAuthenticationEventPublisher {
   CustomAuthenticationEventPublisher() {
   println('CustomAuthenticationEventPublisher')
   Properties exceptionMappings = new Properties()
   exceptionMappings.setProperty(BadOtpException.class.name,  AuthenticationFailureBadCredentialsEvent.class.name)
     super.setAdditionalExceptionMappings(exceptionMappings)
 }

 CustomAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
     super(applicationEventPublisher)
   
 }
}

Thanks.