Search code examples
javaexceptionspring-security

Spring Security Authentication Provider exception handling


I have an authentication provider, that throwing my custom exception. This provider validating token on every request to controllers. Exceptions in controllers handling by controller advice, but provider works before controller, so controller advice cant handle exceptions that provider throws. How can i handle exception from provider?

Provider

@Component
@RequiredArgsConstructor
public class BearerTokenAuthenticationProvider implements AuthenticationProvider {

private final Wso2TokenVerificationClient client;

@Override
public Authentication authenticate( Authentication authentication ) {
    BearerTokenAuthenticationToken token = (BearerTokenAuthenticationToken) authentication;
    Map<String, String> requestBody = new HashMap<>();
    requestBody.put( "token", token.getToken() );
    Wso2TokenValidationResponse tokenValidationResponse = client.introspectToken( requestBody );
    if( !Boolean.parseBoolean( tokenValidationResponse.getActive() ) ) {
        throw new AuthenticationException(
            "Token not valid", HttpStatus.UNAUTHORIZED
        );
    }
    DecodedJWT jwt = JWT.decode(token.getToken());
    UserDetails details = new UserDetails();
    details.setId( Long.parseLong(jwt.getClaim( OidcUserClaims.USER_ID ).asString()) );
    details.setEmail( jwt.getClaim( OidcUserClaims.EMAIL ).asString() );
    token.setDetails( details );
    return token;
}

@Override
public boolean supports( Class<?> aClass ) {
    return BearerTokenAuthenticationToken.class.equals( aClass );
}

Security Config

@Configuration
@RequiredArgsConstructor
public class CommonWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

private final BearerTokenAuthenticationProvider bearerTokenProvider;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.headers().contentSecurityPolicy("script-src 'self'");
    http
            .csrf().disable()
            .authorizeRequests(auth -> auth
                    .antMatchers("/public/**").not().hasAuthority("ROLE_ANONYMOUS")
            )
        .and()
        .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
}

@Override
protected void configure( AuthenticationManagerBuilder auth ) throws Exception {
    auth.authenticationProvider( bearerTokenProvider );
}

}


Solution

  • You can add an authenticationEntryPoint to handle custom exception.

    @Configuration
    @RequiredArgsConstructor
    static class CommonWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
    
        private final BearerTokenAuthenticationProvider bearerTokenProvider;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .headers()
                    .contentSecurityPolicy("script-src 'self'");
            http
                .csrf().disable()
                .authorizeRequests(auth -> auth
                     .antMatchers("/public/**").not().hasAuthority("ROLE_ANONYMOUS")
                )
                .oauth2ResourceServer(c -> c.jwt()
                    .and()
                    .authenticationEntryPoint((request, response, authException) -> {
                        //handle CustomAuthenticationException
                        
                        }
                    )
                );
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.authenticationProvider(bearerTokenProvider);
        }
    }
    
    public class CustomAuthenticationException extends AuthenticationException {
        HttpStatus status;
        
        public CustomAuthenticationException(String message, HttpStatus status) {
            super(message);
            this.status = status;
        }
    }