Closed nexus061 closed 9 months ago
Hi, I think so:
we have 3 cases:
AccessDeniedHandler -> responds to the FORBIDDEN 403 case
Customizer<ExceptionHandlingConfigurer
authenticationEntryPoint at the BearerTokenAuthenticationFilter level intercepts an invalid token
I would let you customize the configuration for all three. With the default set to what it uses from the factory Oaut2, I try to jot down this implementation
As a side note, 401
is the expected status for both missing and invalid tokens.
Actually, AccessDeniedHandler
can respond more than 403
: if you have a look at the reactive implementation, it is slightly different from the servlet one and uses the ServerAccessDeniedHandler
instead of the authenticationEntryPoint
to return 401
in case of missing or invalid token and 403
in case of access rule violation:
response.setStatusCode(principal instanceof AnonymousAuthenticationToken ? HttpStatus.UNAUTHORIZED : HttpStatus.FORBIDDEN);
This means that you can process missing tokens with both authenticationEntryPoint
and accessDeniedHandler
, even if the first should be preferred as it is made to process that case => the reactive implementation should be changed to (I will do after this PR for servlet is merged):
authenticationEntryPoint
instead of accessDeniedHandler
to return 401
in case of missing or invalid token (to replace Spring default which is 302 redirect to login
but makes no sense on a resource server)WWW-Authenticate
header as it shouldI'm not sure Customizer<ExceptionHandlingConfigurer>
is designed specifically to intercept the missing token. I think it is made to configure things like authenticationEntryPoint
and accessDeniedHandler
, as you do in the resourceServer
configurer, but for the all SecurityFilterChain
. As we have only Bearer tokens security in the filter-chain we are configuring, doing it as you do in the resourceServer
or doing it in the exceptionHandling
is the same and we don't need to do both.
I suggest that we choose between one of the two:
Customizer<ExceptionHandlingConfigurer>
in the resource server filter-chains with a default being:
@ConditionalOnMissingBean
@Bean
AuthenticationEntryPoint authenticationEntryPoint() {
return (request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Bearer realm=\"Restricted Content\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
};
}
@ConditionalOnMissingBean
@Bean
Customizer<ExceptionHandlingConfigurer
@Conditional(IsJwtDecoderResourceServerCondition.class)
@Order(Ordered.LOWEST_PRECEDENCE)
@Bean
SecurityFilterChain springAddonsJwtResourceServerSecurityFilterChain(
HttpSecurity http,
ServerProperties serverProperties,
SpringAddonsOidcProperties addonsProperties,
ResourceServerExpressionInterceptUrlRegistryPostProcessor authorizePostProcessor,
ResourceServerHttpSecurityPostProcessor httpPostProcessor,
AuthenticationManagerResolver
http.exceptionHandling(exceptionHandlingCustomizer);
ServletConfigurationSupport
.configureResourceServer(http, serverProperties, addonsProperties.getResourceserver(), authorizePostProcessor, httpPostProcessor);
return http.build();
}
- inject both `authenticationEntryPoint` and `accessDeniedHandler` in the resource server filter-chains, keeping the default `exceptionHandling` (remove `Customizer<ExceptionHandlingConfigurer>` from what is injected into the resource server filter-chains), and configure `authenticationEntryPoint` and `accessDeniedHandler` inside `resourceServer` configurer
```java
@ConditionalOnMissingBean
@Bean
AuthenticationEntryPoint authenticationEntryPoint() {
return (request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Bearer realm=\"Restricted Content\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
};
}
@Conditional(IsJwtDecoderResourceServerCondition.class)
@Order(Ordered.LOWEST_PRECEDENCE)
@Bean
SecurityFilterChain springAddonsJwtResourceServerSecurityFilterChain(
HttpSecurity http,
ServerProperties serverProperties,
SpringAddonsOidcProperties addonsProperties,
ResourceServerExpressionInterceptUrlRegistryPostProcessor authorizePostProcessor,
ResourceServerHttpSecurityPostProcessor httpPostProcessor,
AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver,
AuthenticationEntryPoint authenticationEntryPoint,
Optional<AccessDeniedHandler> accessDeniedHandler)
throws Exception {
http.oauth2ResourceServer(server -> {
server.authenticationManagerResolver(authenticationManagerResolver);
server.authenticationEntryPoint(authenticationEntryPoint);
accessDeniedHandler.ifPresent(server::accessDeniedHandler);
});
ServletConfigurationSupport
.configureResourceServer(http, serverProperties, addonsProperties.getResourceserver(), authorizePostProcessor, httpPostProcessor);
return http.build();
}
I think the second one is better, I implement it and push it
I think the second one is better, I implement it and push it
@ch4mpy push done
I have already pushed the same feature for reactive applications. Will release with a bump to Spring Boot 3.1.5 as transient dependency.
I hope to be able to help again in the future!
Don not hesitate to open more tickets or PRs.
I don't know why the 7.1.11
is not available yet from https://repo1.maven.org/maven2/com/c4-soft/springaddons/spring-addons-starter-oidc/ (the maven release to maven central ended successfully).
If it isn't there tomorrow morning, I'll publish a new version with the same content.
I have added possibility to ovveride AuthenticationEntryPoint for excpetion handing and and oauth2ResourceServer