My application is a simple resource server — I am using AadResourceServerHttpSecurityConfigurer.aadResourceServer()
to validate the given access token. The specific documentation I have followed can be found here.
Goal: Return a custom error message when a malformed token (e.g., '123') is given. It does not seem that my accessDeniedHandler
is being called. In contrast, when I don't specify a JWT at all, it works fine and a custom error message is returned (authenticationEntryPoint
is called).
This is the code I am using (I have modified Microsoft's example as it was using deprecated methods):
@Autowired
private ErrorHandler errorHandler;
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrfCustomizer -> csrfCustomizer.disable())
.with(AadResourceServerHttpSecurityConfigurer
.aadResourceServer(), Customizer.withDefaults())
.authorizeHttpRequests(requests -> requests
.requestsMatchers(antMatcher("/users/**")).permitAll()
.requestMatchers(antMatcher(HttpMethod.GET, "/admin")).hasRole(ADMIN_ROLE)
.anyRequest().authenticated()
)
.exceptionHandling(exceptionHandlingCustomizer -> exceptionHandlingCustomizer.authenticationEntryPoint(errorHandler))
.exceptionHandling(exceptionHandlingCustomizer -> exceptionHandlingCustomizer.accessDeniedHandler(errorHandler))
.sessionManagement(sessionManagement -> sessionManagementCustomizer.sessionCreationPolicy(sessionCreationPolicy.STATELESS)));
return http.build();
}
The specific error handler (simplified):
@Configuration
public class ErrorHandler authenticationEntryPoint, AccessDeniedHandler {
@Override
public void commence() {
// this is called
}
@Override
public void handle() {
// this is not called
}
}
In contrast, this configuration works fine and a custom error message is returned when I provide a malformed token, but I'm not utilising AadResourceServerHttpSecurityConfigurer.aadResourceServer()
:
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrfCustomizer -> csrfCustomizer.disable())
.oauth2ResourceServer(httpSecurityOAuth2ResourceServerConfigurer -> httpSecurityOAuth2ResourceServerConfigurer
.authenticationEntryPoint(errorHandler)
.accessDeniedHandler(errorHandler)
)
.authorizeHttpRequests(requests -> requests
.requestsMatchers(antMatcher("/users/**")).permitAll()
.requestMatchers(antMatcher(HttpMethod.GET, "/admin")).hasRole(ADMIN_ROLE)
.anyRequest().authenticated()
)
.sessionManagement(sessionManagement -> sessionManagementCustomizer.sessionCreationPolicy(sessionCreationPolicy.STATELESS)));
return http.build();
}
Is there a way to utilise AadResourceServerHttpSecurityConfigurer.aadResourceServer()
with both the authenticationEntryPoint
and accessDeniedHandler
exception handlers?
but I'm not utilising
AadResourceServerHttpSecurityConfigurer.aadResourceServer()
I would not set using AadResourceServerHttpSecurityConfigurer
as a requirement.
Microsoft proprietary Boot starters are randomly maintained and documented. It sometimes took a while before they published versions compatible with new Spring Security versions, and it's not always quite clear what versions a documentation page / starter lib is compatible with. For this reasons, I prefer to use just Spring Boot official starters (spring-boot-stater-client
or spring-boot-stater-resource-server
), optionaly with an additional starter of mine which is compatible with any OIDC authorization server (AAD, as well as Keycloak, Auth0, Amazon Cognito,...)
So, your second conf is a better starting point. "My" Boot starter can help replace Java security conf with just application properties (and implement rather tricky features like cookie-based CSRF protection, authorities mapping, multi-tenancy, changing authorization code flow redirection statuses, and more).
401
is what a resource server should return in case of a missing or invalid token (expired, wrong issuer or audience, etc.). So ensure that you change no more than the error message.