-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Description
Expected Behavior
Consider the following security config, taken from documentation
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/messages/**").access(hasScope("message:read"))
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.jwtAuthenticationConverter(myConverter())
)
);
return http.build();
}
I expect the following snippet should work out of the box (or with minimal effort)
@WebMvcTest
...
mockMvc.perform(
get("/messages/foo").with(jwt().jwt(builder -> builder.claim("message", "read").build())))
.andExpect(status().isOk());
Current Behavior
- Vanilla
application.properties: spring.security.oauth2.resourceserver.jwt.issuer-uri= ${ISSUER_URI}
Caused by: org.springframework.util.PlaceholderResolutionException: Could not resolve placeholder 'ISSUER_URI' in value "${ISSUER_URI}"
- Vanilla + excludeAutoConfiguration = OAuth2ResourceServerAutoConfiguration.class
No qualifying bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' available
Context
- Vanulla + excludeAutoConfiguration = OAuth2ResourceServerAutoConfiguration.class + @MockitoBean JwtDecoder jwtDecoder;
BUILD SUCCESS
Context
Security is authentication and authorization.
Very often external service - here ISSUER_URI - is responsible for authentication.
But authorization - here "/messages/**").access(hasScope("message:read")
- is completely under developer control.
That is a reason why developer wants test something he control and does not test something he is not responsible.
Although @WebMvcTest
is Annotation that can be used for a Spring MVC test that focuses only on Spring MVC components., the annotation is enriched (if possible) with some default security.
It is (more or less) reasonable to extent scope of @WebMvcTest
for testing authorization (controlled), but not for authentication (uncontrolled)
Unfortunately, in 2020 in spring-projects/spring-boot#19823 it was reported that OAuth2AuthorizedClientArgumentResolver is not automatically added to @WebMvcTest
. Instead of adding the resolver, autoconfiguration for OAuth2Client was added and for OAuth2Resource also, although OAuth2Resource is not related to OAuth2AuthorizedClientArgumentResolver
Finally, a developer which want to test his authorization code with @WebMvcTest
, must focus on tame emerging authentication.
In 2022 in #11784 (smaller number, later issue) it was reported that @WebMvcTest
unnecessary awakes OAuth2Client authentication.
But it was too late. Phrase from Javadoc By default, tests annotated with @WebMvcTest will also auto-configure Spring Security closes a discussion. Fairly speaking, it should be told that @WebMvcTest awakes OAuth2Client to register OAuth2AuthorizedClientArgumentResolver.
Conclusion
In my opinion authentication OAuth2ResourceServerAutoConfiguration in @WebMvcTest is a mistake. The same applies to other autoconfigurations responsible for authentication.
- If someone wants test his authentication, he is be responsible for adding OAuth2ResourceServerAutoConfiguration to his WebMvcTest.
- If someone wants test his RestController, which consumes JwtAuthenticationToken or OAuth2User, he should be able to do this without taming emerging authentication
- If someone wants test his authorization, it should be simple and supported by spring-security-test. For OAuth2Resource, it could be something like
public class MockOAuth2ResourceAutoConfiguration {
@Component
@ConditionalOnClass(JwtDecoder.class)
@ConditionalOnMissingBean(JwtDecoder.class)
static class TestJwtDecoder implements JwtDecoder {
@Override
public Jwt decode(String token) throws JwtException {
return null;
}
}
}
It seems overengineering, but analogous MockOAuth2ClientAutoConfiguration should register OAuth2AuthorizedClientArgumentResolver and probably more (I don't know OAuth2Client well)
If the enhancement is accepted, I may implement it.