Reputation: 49
I want to send some data in JSON from my React front-end on port 3000 using fetch, to my node.js server on 3005. I configured cors on my server, but every time I try to send request with cookies, Chrome throws error:
Access to fetch at 'http://localhost:3005/user-connected' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
All console.log's from server code are skipped.
When I delete header in my fetch code
"Content-Type", "application/json"
I get cookies, but without data. With this header included, but without credentials: "include", I can get my data, but I'll never get both at the same time.
Here's my fetch code:
fetch("http://localhost:3005/user-connected", {
mode: "cors",
method: "post",
headers: [
["Content-Type", "application/json"],
],
credentials: "include",
body: JSON.stringify({data: "123"})
})
.then(data => data.json())
.then((data) => {
console.log(`Response: ${JSON.stringify(data)}`);
}).catch(err => {
console.log(`Error: ${err}`)
});
Node.js cors configuration:
app.use(cors({credentials: true}));
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,Content-Type', 'Access-Control-Allow-Origin', 'Origin');
res.setHeader('Access-Control-Allow-Credentials', true);
next();
});
And my post route:
let cookiesData = (req, res) => {
console.log(`Cookie user-connected: ${req.cookies.io}`)
console.log(`Received data: ${JSON.stringify(req.body)}`);
res.send({ status: "OK" });
}
router.post("/user-connected", cors({origin: 'http://localhost:3000'}), cookiesData);
Is it even possible to do what I want? Or maybe I missed some important configuration?
Upvotes: 4
Views: 11833
Reputation: 21
Adding a Content-Type will trigger a preflight request first before sending the actual request. Preflight requests method is OPTIONS. OPTIONS doesn't include cookies in the request header (at least for Chrome and Firefox).
The error is probably because of this code:
console.log(`Cookie user-connected: ${req.cookies.io}`)
req.cookies is null during preflight requests and will throw an error when you try to get req.cookies.io. Preflight requests require the server to return 200 (see https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests). Your code having errors might be throwing 500 thus showing you the dreaded CORS error message.
You must deal with preflight requests first.
On the server side, try handling the OPTIONS method in your CORS code to return a 200 before next():
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,Content-Type', 'Access-Control-Allow-Origin', 'Origin');
res.setHeader('Access-Control-Allow-Credentials', true);
if (req.method === 'OPTIONS') {
res.sendStatus(200);
return;
}
next();
});
This will prevent whatever code next() will execute during preflight requests and will tell the client that it is OK to send the actual request.
Upvotes: 1
Reputation: 11
you need to pass headers like this in your fetch request for post method:
{
credentials: 'include',
mode: 'cors',
method: 'POST',
headers: { 'Content-Type': 'application/json' },
}
You must be sure that you have given access allow origin for localhost:3000
Upvotes: 1
Reputation: 138257
Whenever an error occurs during the connections between the frontend and the backend, it is usually a good idea to seperate the problem into those ends, so at first let's completely ignore the frontend. Instead we can query the backend with curl or Postman, through that you can get an insight into what the server actually returns.
Now in your case, the CORS pre-flight request is the one that counts, and that can be checked if it is correctly set up by sending an OPTIONS request to the specific server path.
You will then find out, that Wildcards and credentials can't be used at the same time.
Now if you change the cors({ ... })
options on the route itself you'll find out that this does not influence the actual OPTIONS header returned. That is, because the first cors()
will terminate all OPTION requests and will answer with uts configuration, all further cors()
middlewares won't change anything.
Therefore you should either add a global cors handler:
app.use(cors({origin: 'http://localhost:3000', credentials: true }));
// don't use cors() again!
or (which is probably better as it allows for fine grained access control) add the cors setting to each route on its own:
app.use("/route", cors({origin: 'http://localhost:3000'}));
app.get("/route", (req, res) => { /*...*/ });
You could also add it to a Router, so that the cors policy affects only a certain path.
Upvotes: 0
Reputation: 2163
try to use headers as an object:
headers: {
"Content-Type": "application/json",
},
the documentation Fetch Reference - Headers shows examples with object, not arrays
Upvotes: 0