Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring security - oauth2 resource server tests

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?

like image 709
henriquels Avatar asked Jul 18 '19 22:07

henriquels


1 Answers

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.

like image 136
Eleftheria Stein-Kousathana Avatar answered Oct 16 '22 08:10

Eleftheria Stein-Kousathana