spring-security: Spring Security 6.x / Single Page Web Application / CSRF - formLogin not working anymore

Describe the bug I have a Spring Boot 3.x application (with Spring Security 6.x). The frontend is Angular 15.2.x. I am following the instructions here to enable CSFR as well as allow post requests from Angular.

While this works, it has the issue if I use the default Spring Security Configuration in Spring Boot (form login) then after successful login I am logged in to the same Login page - ie I cannot access the application.

To Reproduce Create a simple new Spring Boot 3.x application. Configure Security according to here:

[..]
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
    XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
        
    // set the name of the attribute the CsrfToken will be populated on
    delegate.setCsrfRequestAttributeName("_csrf");
     
    // Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
    // default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
    CsrfTokenRequestHandler requestHandler = delegate::handle;
        
    http.authorizeHttpRequests().anyRequest().authenticated().and().formLogin().and().httpBasic().and()
        .csrf((csrf) -> csrf
            .csrfTokenRepository(tokenRepository)
            .csrfTokenRequestHandler(requestHandler)
        
                );
              
        return http.build();
[..]

If you do gradlew bootRun and you try to login then after successful login you are redirected to the login page again. If I do NOT have the additional configuration with CSRF then I can login normally (but then I have the issue to do Post requests in Angular).

Edit: Apparently the /login results in an invalid CSRF token foudn issue when submitting the credentials

Expected behavior After successful login it redirects to my application and not back to /login.

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 20 (6 by maintainers)

Most upvoted comments

@EtienneKaiser, the code .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class) simply sets the filter order so the custom filter would appear after BasicAuthenticationFilter in the filter chain. This works even if BasicAuthenticationFilter is not present. You don’t need to add it for this to work.

Please see the Single-Page Applications section of the CSRF chapter of the docs for more information. If you need additional help, please open a question on stackoverflow. This issue is not intended to be a support forum, and I would ask you to try your best not to use it for that purpose.

I am not sure if I can understand your issue correctly. I assume you want to work with a Javascript framework that needs to CRFS token refreshed on every request and has it in its cookies accessible to Javascript for XHR requests. This is an excerpt from working code (it has been according to Spring Boot 3 standards, but maybe not the latest 3.2, but in any case should work.

        CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
        XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
        // set the name of the attribute the CsrfToken will be populated on
        delegate.setCsrfRequestAttributeName("_csrf");
        // Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
        // default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
        CsrfTokenRequestHandler requestHandler = delegate::handle;
        http.csrf(
                        (csrf) ->
                                csrf.csrfTokenRepository(tokenRepository)
                                        .csrfTokenRequestHandler(requestHandler))
                .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class);
        return http.build();
    }
    // We have to add this filter to refresh the CSFR token every time - otherwise the first post
    // after SAML login will fail
    private static final class CsrfCookieFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(
                HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
            CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
            // Render the token value to a cookie by causing the deferred token to be loaded
            csrfToken.getToken();
            filterChain.doFilter(request, response);
        }
    }

 

@jornfranke, I have updated the main CSRF chapter of the reference documentation for the 6.1 release. You can preview it here: https://docs.spring.io/spring-security/reference/6.1-SNAPSHOT/servlet/exploits/csrf.html

Your feedback is most welcome on the updates, and we can keep improving.

In the meantime, I’m going mark as a duplicate of gh-13089 and close this issue for now, as I believe there is no additional work to be done with the docs at this time. If there’s something you feel I’ve missed in the update, or you have specific suggestions for the 5.8 migration guide, let me know and we can re-open or open a new specific issue for it. Thanks!