I am having some problems when testing an oauth2 resource server using @WebMvcTest and the POST HTTP method.
I always receive a 403 status code when I don't send the csrf token, even though the token is not required when I am using a bearer token.
Here is the POST method that I want to test.
@PostMapping("/message")
public String createMessage(@RequestBody String message) {
return String.format("Message was created. Content: %s", message);
}
Here is my security config:
http.authorizeRequests(authorizeRequests -> authorizeRequests
.antMatchers("/message/**")
.hasAuthority("SCOPE_message:read")
.anyRequest().authenticated()
).oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer
.jwt(withDefaults())
);
I am following the tests provided in the samples of spring-security.
The following test was supposed to pass but it fails because the csrf token is not sent in the request.
mockMvc.perform(post("/message").content("Hello message")
.with(jwt(jwt -> jwt.claim("scope", "message:read")))
.andExpect(status().isOk())
.andExpect(content().string(is("Message was created. Content: Hello message")));
When I add the csrf token to the request, the test passes:
mockMvc.perform(post("/message").content("Hello message")
.with(jwt(jwt -> jwt.claim("scope", "message:read")))
.with(csrf()))
.andExpect(status().isOk())
.andExpect(content().string(is("Message was created. Content: Hello message")));
When I run the application, there is no need to send a csrf token in the POST request.
I have forked the Spring Security GitHub repository and the project with this failing test is available at this link.
Is there a way for me to configure my tests so I don't need to send the csrf token in the POST request?
In order for the CSRF filter to detect that you are using a JWT token, you will need to include the JWT token in your request as an Authorization
header, or as a request parameter.
The tests that you have mentioned have a mock JwtDecoder
, which means you can use any string as your token and mock the decoded value.
Your test would then become:
Jwt jwt = Jwt.withTokenValue("token")
.header("alg", "none")
.claim("scope", "message:read")
.build();
when(jwtDecoder.decode(anyString())).thenReturn(jwt);
mockMvc.perform(post("/message")
.content("Hello message")
.header("Authorization", "Bearer " + jwt.getTokenValue()))
.andExpect(status().isOk())
.andExpect(content().string(is("Message was created. Content: Hello message")));
If you are not mocking the JwtDecoder
then you would need to retrieve a valid bearer token and pass that in the Authorization
header.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With