Michael
Michael

Reputation: 4351

Invalid CSRF token found - Spring Boot and Axios

I would like to post using Axios to my Spring Boot server. If I disable csrf using .csrf().disable() it works correctly however it fails when enabled.

I've tried adding X-CSRF-TOKEN to the header, or _csrf in the body but it is saying it is invalid. Checking the request the csrf is being passed in as expected.

CSRF Controller

@RequestMapping("/csrf")
public String csrf(final CsrfToken token) {
    return token.getToken();
}

Spring Security

httpSecurity
        .antMatcher("/api/**")
        .cors().and()
        .authorizeRequests()
        .anyRequest().authenticated()
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

Axios

axios
    .post(
      `/api/doSomething`,
      { id: id, _csrf: csrf},
      {
        withCredentials: true,
        headers: {
          'X-CSRF-TOKEN': csrf
        }
      }
    )
    .then(response => {
      resolve(response.data);
    })
    .catch(error => {
      reject(error);
    });

MetaTags react-meta-tags store the CSRF

<MetaTags>
  <meta name="csrf-token" content={csrfToken} />
</MetaTags> 

Axios to get the CSRF token

axios
.get('/csrf', {
  withCredentials: true
})
.then(response => {
  resolve(response.data);
})
.catch(error => {
  reject(error);
});

Function to get the CSRF token from the meta tags

function getCSRFFromPage(): string | null {
  const element = document.querySelector("meta[name='csrf-token']");

  if (element !== null) {
    const csrf: string | null = element.getAttribute('content');
    return csrf;
  } else {
    return null;
  }
}

Error

Invalid CSRF token found for <url>

What could be causing this to fail?

Upvotes: 2

Views: 7258

Answers (1)

Dimitri Mestdagh
Dimitri Mestdagh

Reputation: 44675

There are two possible causes.

First of all, the CSRF token endpoint should match the Spring Security configuration. In your example, you're using antMatcher("/api/**"), but CSRF token endpoint is /csrf. This should likely become /api/csrf.

The second part is that the CSRF token changes after each request. You didn't show the code where you invoke getCSRFFromPage(), but this should happen before each call you make. If not, then only the first call with that CSRF token will succeed. All consecutive calls will fail.

If you don't like making an additional call with each request, then you can use the CookieCsrfTokenRepository:

httpSecurity
    // ...
    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

By doing so, each time you make a request, you'll get an XSRF-TOKEN cookie containing the next CSRF token. This should be passed within the X-CSRF-TOKEN or X-XSRF-TOKEN header. Many libraries, including Axios do this out of the box, so you don't have to do anything else.

You can customize this behavior by configuring the xsrfHeaderName or xsrfCookieName properties (see Request Config).

Upvotes: 1

Related Questions