1
votes

I'm trying to throw a custom exception in WebFlux during authentication, and handle it with a ControllerAdvice (@ExceptionHandler). Unfortunately, it doesn't get propagated, I'm getting either HTTP 500 if I throw the exception, or HTTP 401 if I return the exception as Mono.error()

@Override //in authentication service
public Mono<UserDetails> findByUsername(String username) {
    //find user by username from database, 
    //if not enabled, throw a custom exception, 
    //if doesn't exist, throw UsernameNotFoundException, 
    //return org.springframework.security.core.userdetails.User otherwise.
}

@ExceptionHandler //in controller advice
public Mono<HttpStatus> handleException(MyCustomExceptionThrownFromFindByUsername ex) {
    //implemented
}

Is there any way to help the exception to make it to the ExceptionHandler?

1
Why? The check for using an enabled user is already done by Spring Security for you so why do it here in a place that isn't responsible for that.M. Deinum
@M.Deinum actually it does not, or I'm doing something wrong, org.springframework.security.core.userdetails.User only cares about username, password and authorities.OCPi
No it doesn't... It implements UserDetails which has isEnabled... There is a constructor that takes a 4 additional booleans to indicate different user states. Default for all of them is true. Use the proper constructor and let Spring Security handle the rest.Also regardless of the error Spring Security will issue 401. You don't want to provide more details from a security perspective... (If a hacker does a brute force and gets more information like account is disabled he knows that it is an existing account).M. Deinum
@M.Deinum even if I'm using a wrong UserDetails implementation for this, I'm curious how my exception disappears, I'm still learning the framework.OCPi
@M.Deinum I overlooked the other constructor somehow. :) If you add this as answer I'll accept it.OCPi

1 Answers

0
votes

The UserDetailsService (both reactive and non-reactive) has as a job to retrieve the user based on the username. Nothing more and nothing less. Checking if the user is enabled is delegated to a UserDetailsChecker which calls some methods on the UserDetails implementation and will react accordingly. Don't try to do more in here as that isn't the task of the UserDetailsService.

The default UserDetails, User implementation has 2 constructors, one with 3 and one with 7 parameters. Use the second one to fill the enabled properly according to your business rules and Spring Security will do the rest as it should.