Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React fetch, “credentials: include”, breaks my entire request and I get an error

Summary:

I'm doing a fetch request in React to my Node.js server.

Whenever I do NOT include credentials: "include" and in my fetch request, the request is successfully made to the server and returned to the client.

However, when I do include credentials: "include", like the below:

fetch('http://localhost:8000/',
    {   method: "GET", 
       'credentials': 'include',
        headers: new Headers({
            'Accept': 'application/json',
            'Access-Control-Allow-Origin':'http://localhost:3000/',
            'Content-Type': 'application/json',
    })

}) ....

I get this preflight error:

login:1 Access to fetch at 'http://localhost:8000/' 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'.

Context:


Why do I need to include either of those?

  1. I think it's obvious why I need to include the "headers", I'm using cors and if I don't include 'Access-Control-Allow-Origin':'http://localhost:3000/' then the server will not accept the request.
  2. Why do I need to include the "credentials" if it works without it? Because if I do not include "credentials" while the fetch request executes correctly, the session cookie will not be sent to the server from my client UNLESS I include credentials: "include". If I delete all the headers and include mode: 'no-cors', then the fetch request executes and the session cookie is sent to the server, but obviously I get an opaque response, and I need to be using cors anyways.

Attempts:


There are a lot of stack overflow questions SIMILAR to this, but not exact, thus their solutions don't work.

Here are some things I have tried that didn't work:

  1. This is already on my server, but someone suggested trying it on the client side so I did: 'Access-Control-Request-Method': 'GET, POST, DELETE, PUT, OPTIONS',

  2. 'Access-Control-Allow-Credentials': 'true',

  3. 'withCredentials': 'true',

  4. Origin: 'http://localhost:3000/auth',

  5. crossorigin: true,

  6. And yes, I've already set up a proxy (which helped solve a prior issue) as such: "proxy": "http://localhost:8000"

  7. I've tried many more other solutions to no avail, I'm certain I've read, if not all, the vast majority of all questions relating to do with this issue and the corresponding answers. My server is setup correctly, which is why I didn't include any code from it.

In an ideal world I wouldn't need to use credentials: "include" for the session cookie to be sent back to my server, but that is the cause of another solution I had to implement.

If anyone could help me, I would be very grateful.

TLDR:


My preflight request does pass whenever I do NOT include credentials: "include", but the session cookie is not passed.

The session cookie is passed when I do include credentials: "include" and mode: 'no-cors', however, I receive an opaque response and I need to use cors.

Finally, when I combine the two (cors and credentials), I my preflight request fails with the below error:

login:1 Access to fetch at 'http://localhost:8000/' 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'.

like image 877
ezg Avatar asked Aug 11 '20 05:08

ezg


People also ask

How do I fix failed to fetch error?

To solve the "TypeError: Failed to fetch", make sure to pass the correct configuration to the fetch method, including the URL, HTTP method and headers, and verify that the server you're making a request to is setting the correct CORS headers with the response.

When the request's credentials mode is include?

credentials is “include”. Remember one thing when the Request. credentials is “include” mode browsers will expose the response to front-end JavaScript code if the Access-Control-Allow-Credentials is set true. The Access-Control-Allow-Credentials header performs with the XMLHttpRequest.

What does credentials include mean?

credentials ) is include . When a request's credentials mode ( Request. credentials ) is include , browsers will only expose the response to the frontend JavaScript code if the Access-Control-Allow-Credentials value is true . Credentials are cookies, authorization headers, or TLS client certificates.

How to fetch data with ReactJS?

The most accessible way to fetch data with React is using the Fetch API. The Fetch API is a tool that's built into most modern browsers on the window object ( window.fetch ) and enables us to make HTTP requests very easily using JavaScript promises.

How do I send a get error in react?

GET request using fetch with error handling This sends a GET request from React to an invalid url on the npm api then assigns the error to the errorMessagecomponent state property and logs the error to the console. The fetch()function will automatically throw an error for network errors but not for HTTP errors such as 4xx or 5xx responses.

How do I send HTTP requests to the API in react?

HTTP requests to the API are sent with the fetch wrapper. The getAll () method is called from a secure page in the React example app after the user has logged in, so the fetch wrapper automatically sets the HTTP Authorization header of the request to the JWT token of the authenticated user.

What is the use of the fetch wrapper in react?

It handles communication between the React app and the backend api for everything related to users, and also handles Recoil state update operations for users and auth atoms. HTTP requests to the API are sent with the fetch wrapper.


Video Answer


2 Answers

this most likely comes from your server. Do you have cors npm package installed in the backend ?

https://www.npmjs.com/package/cors

You will need ton configure it aswell.

Most likely in your index.js file.

const express = require("express")
const cors = require("cors");
const app = express();

app.use(cors({
  origin : http://localhost:3000 (Whatever your frontend url is) 
  credentials: true, // <= Accept credentials (cookies) sent by the client
})

app.use("/api/whatever/the/endpoint", yourRouter);

This has to be set before any route. Origin can be an array of whitelisted (allowed) domains to communicate with your backend api.

like image 86
yeeeehaw Avatar answered Oct 21 '22 06:10

yeeeehaw


The correct explanation here is that the server was sending back the header Access-Control-Allow-Origin: * in the response (as described in the error message).

Without credentials this is acceptable. However, to quote the Mozilla CORS documentation,

When responding to a credentialed request, the server must specify an origin in the value of the Access-Control-Allow-Origin header, instead of specifying the "*" wildcard.

Furthermore, if you were already using the npm cors module to handle setting the response headers, note that

The default configuration is the equivalent of:

{
  "origin": "*",
  "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
  "preflightContinue": false,
  "optionsSuccessStatus": 204
}

So you have to explicitly configure it. This is why @yeeeehaw's answer worked - they suggested explicitly setting the origin option which translates into setting Access-Control-Allow-Origin behind the scenes.

Note that as an alternative solution, instead of explicitly setting origin (i.e. Access-Control-Allow-Origin) you can reflect the request's origin back as its value. The cors middleware conveniently provides for this through its configuration.

Boolean - set origin to true to reflect the request origin, as defined by req.header('Origin'), or set it to false to disable CORS.

origin: true

On Stack Overflow this has also been described here, and on the reverse proxy level here (for NGINX). Maybe the most similar question is here.

like image 26
Ders Avatar answered Oct 21 '22 04:10

Ders