Search code examples
jsontypescriptspringrestspring-security

Why is my RESTful web service returning error 401?


Update

Thanks to being nudged towards implementing debug logs, I can now see that the issue is that Spring is reporting an invalid CSRF token found for the notices controller. So far I've checked the headers postman generates and compared them to the ones generated through the fetch requests, and, found no difference. The token that was generated is successfully placed into the header of the request. Unfortunately there is nothing discernible in the Spring logs, so the debugging continues.


I'm working on learning Spring Security and currently connecting the frontend React portion to the Spring backend. I'm having trouble because when the POST request is made to the desired endpoint, it returns an error of 401. This is confusing to me because I believe I have correctly configured CORS and also marked the end points as permit all.

In short, the process calls an endpoint /token to get a CSRF token, then calls /notices and passes the token in as a header. If done with Postman, the process works as expected, so I had thought it was a CORS issue, however, I've tried running the frontend on a different port and it was blocked by CORS so I think the issue is somewhere else.

Some additional info:

  • /notices and /token are both POST operations.
  • Both the Spring backend and React frontend are ran off the same local machine.
  • Error code 401 is received

The code for the frontend JavaScript call is:

const debugNotices = () => {
  let tokenData:any;
  fetch('http://localhost:8080/token', {method:"POST"})
  .then((response) => response.json())
  .then((data) => tokenData = data).then((data:any) => fetch("http://localhost:8080/notices", 
  {
    method:"POST",
    headers: {
      "X-XSRF-TOKEN": tokenData.token
    }
  }))

}

Spring security config:

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        
    http
        .cors()
            .configurationSource(new CorsConfigurationSource() {
            
                @Override
                public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                    CorsConfiguration config = new CorsConfiguration();
                    
                    config.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
                    config.setAllowedMethods(Collections.singletonList(("*")));
                    config.setAllowCredentials(true);
                    config.setAllowedHeaders(Collections.singletonList("*"));
                    config.setMaxAge(3600L);
                    return config;
                }
            })
            .and()
        .csrf()
            .ignoringRequestMatchers("/contact", "/register", "/token")
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) 
            .and()
        .securityContext()
            .requireExplicitSave(false)
            .and()
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            .and()
        .authorizeHttpRequests()
            .requestMatchers("/myAccount", "/myBalance", "/myLoans", "/myCards", "/user").authenticated()
            .requestMatchers("/notices", "/contact", "/register", "/test", "/token").permitAll()
            .and()
        .httpBasic()
            .and()
        .formLogin();
        
    return http.build();
}

I've tried including credentials:'include' in the request body, however it causes a login prompt that I don't believe is the direction I'm looking for.

I've also tried manually inserting the CSRF token instead of requesting the data from the server, with the same failed results.

I've also tested CORS as far as I know how to, accessing the endpoints from anything other than localhost:3000 gets denied with a CORS error as expected.


Solution

  • This issue only happens when the React frontend is accessed from localhost:portNumber, I was able to work around the issue completely by instead using my local IP address in place of localhost.

    192.168.0.105:3000 for example.

    I'm still unsure why there is an issue using localhost in the URL, and, would love to hear why this is happening if you know.