Describe the bug In project with spring security and thymeleaf on servlet stack there is different behaviour in case of missing CSRF token when attempting login.

To Reproduce Run provided sample and send request from post-login.http file without csrf token, when csrf filter is enabled:

# @no-redirect
POST http://localhost:8080/login
Content-Type: application/x-www-form-urlencoded

username=admin&password=hardPassword

Then change the version of Spring Boot to 3.0.2 run again, and send request again.

Response for Spring Boot 2.7.5 is 403 (in the browser it is error page) Response for Spring Boot 3.0.2 is 302 Found /login (in the browser it is login form)

For some reason MockMvc gets 403 response in both cases as you can see in provided test cases.

Expected behavior Same response in both cases.

Sample https://github.com/slawekludwiczak/spring-security-csrf You can check behaviour on youtube https://youtu.be/K4mYYQUWE9s

Comment From: marcusdacoregio

Hi @slawekludwiczak, this is an expected scenario.

In Spring Security 6.0, there was a change to perform authorization for every dispatcher type, Spring Boot also changed its defaults.

In summary, Spring Boot redirects to /error after the CSRF 403 and: - In Spring Security 5, the ERROR dispatch is not protected and returns the 403 response. - In Spring Security 6, the /error is protected and you did not permit the /error endpoint or the DispatcherType.ERROR, therefore the application will return 401 for the /error and Spring Security understands a 401 as an unauthenticated login and redirects to the /login page.

You have a few workarounds for this:

  1. Add .requestMatchers("/error").permitAll() to your authorization rules
  2. Add .dispatcherTypeMatchers(DispatcherType.ERROR).permitAll() to your authorization rules
  3. Remove error from spring.security.filter.dispatcher-types although not recommended

Comment From: slawekludwiczak

Hi @marcusdacoregio can you please verify that mockMvc works as expected in this situation? I would expect that for missing csrf() I will get 302 response, but i get 403 both for spring boot 2.7.x and 3.0.x The test case is provided in sample and simply looks like this

mockMvc.perform(post("/login")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .param("username", "admin")
            .param("password", "hardPassword"))
//          .with(csrf()))
.andExpect(status().isFound());

Comment From: marcusdacoregio

Hi @slawekludwiczak,

I would expect that for missing csrf()

I don't think I follow because your test has with(csrf()). Also, I don't understand why you expect a 302, if you do not provide the CSRF token you will have your access denied, therefore a 403 should be expected.

It is also important to mention that MockMvc is different than end-to-end tests:

Things that may catch you by surprise are that there is no context path by default; no jsessionid cookie; no forwarding, error, or async dispatches;

If you want another behavior from MockMvc I recommend you to read the link above and maybe try another approach.

Comment From: slawekludwiczak

with(csrf()) is commented. Thanks for the link, I will check that out

Comment From: marcusdacoregio

I'm sorry, I didn't notice the // 😄