Home > Enterprise >  How to authenticate properly in post request with fetch api?
How to authenticate properly in post request with fetch api?

Time:10-19

  • I have a SpringBoot Backend with Spring Security Enabled in the default configuration (nothing changed there).

  • I have following Rest Controller and Post Mapping:


@RestController
public class MyController {

    @PostMapping(value = "/sm/resrc/pth")
    public Integer postSomething(@RequestParam String someValue,@RequestParam String userId){
        System.out.println(String.format("SomeValue: %s from userid %s",someValue,userId));
        return 0;
    }

}
  • Creating a Post request from the following form works fine:
<form method="post" th:action="@{/sm/resrc/pth}">
     <input type="text" name="someValue">
     <input type="text" name="userId">
     <input type="hidden" name="_csrf" value="${_csrf.token}" />
     <div><input type="submit" value="Send" /></div>
</form>

It does even work without the hidden cors value.

  • However I need to create a post request from JavaScript, which doesn't work. The SpringBoot application is running at localhost:8080. I thought, the credentials parameter 'include' is used for including the required authentication headers, which the user already entered successfully to open the given page. Is this correct? I also changed the value of 'mode'. I tried 'cors', 'same-origin' and 'no-cors'. It just does't work. I event don't understand why cors is a problem anyway as I am requesting a resource from the same origin. It didn't even work after adding the Authorization header manually to the request without using the credentials parameter.As you can see in the image I always get the 403 status. What is wrong with my request? What am I missing?
let data = {
            "some":"abc",
            "values":this.localDescription,
            "userId":userId
        };

fetch("sm/resrc/pth",
            {
                method: 'POST',
                credentials: 'include',
                mode: 'cors',
                body: data
            }
        ).then(response => console.log(response));

screenshot of response

CodePudding user response:

I've found the same problem doing a login example with jax-rs. In my case the problem was that when add the 'credentials': 'include' to the request, on the side of the Rest Service API, need configure in the CORS Filter the header from:

response.addHeader("Access-Control-Allow-Origin", "*");

to:

res.addHeader("Access-Control-Allow-Origin", "http://localhost");

Where "http://localhost is the origin of my request.

I've found information about it in this post: No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API

CodePudding user response:

CORS is a security feature; So you probably don't want to disable or bypass it. I struggled a bit to find out, how to include the headers correctly but finally found the solution. That's how I solved it:

In the first step I added the expected CSRF-Header-name and the token itself to the metadata of the generated html file. In spring using the templating framework thyemleaf it looks like following:

<html lang="en">
    <head>
        <meta name="_csrf_header_name" th:content="${_csrf.headerName}"/> 
        <meta name="_csrf_token" th:content="${_csrf.token}"/>
               
    </head>
    .
    .
    .
</html>

Which results in something like that:


<meta name="_csrf_header_name" content="X-CSRF-TOKEN">
<meta name="_csrf_token" content="14d98c88-8643-1234-5678-39473aa7890e">

In the fetch request, the CSRF Header is built from the token and the correct Header-name (which are read from the meta tags) just as expected from the server.

getCsrfToken(){
   return document.querySelector('meta[name=_csrf_token]').content;
}

getCsrfTokenName(){
   return document.querySelector('meta[name=_csrf_header_name]').content;
}
 
let csrfToken = this.getCsrfToken();
let csrfTokenName = this.getCsrfTokenName();
        
fetch('sm/resrc/pth', {
          method:'post',
          headers: new Headers([[csrfTokenName, csrfToken]])
        }).then(res =>{console.log(res);});}
  • Related