I'm hosting my express API on Heroku and my client on Netlify. When I try my signup route locally, my cookie is defined and the route works. However, when it is in production, the cookie always returns undefined and my API times out.
Note that the cookie is being sent successfully from the backend. I’m able to view it in Dev Tools. Additionally, cookies,get() returns an empty object.
I’m using Js-cookie.
I'm using js-cookie in Gatsby. I'm using CSURF in express for the cookie.
Backend:
//CSURF Config
app.use(csurf({ cookie: true }));
//Route that generates CSRF Cookie
app.get("/getToken", (req, res) => {
res.cookie("XSRF-TOKEN", req.csrfToken());
res.end();
});
Frontend:
I'm including the entire sign up function. Note that this is two end point calls, one to retrieve the cookie, and one to create user record.
userSignUp = async (email, password, resetForm) => {
console.log("THis is a test of the emergency..")
await fetch(process.env.API + "getToken", {
mode: "cors", // no-cors, cors, *include
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "include", // include, *include, omit
headers: {
"content-type": "application/json",
},
method: "GET",
})
const token = Cookies.get("XSRF-TOKEN")
alert(Cookies.get("XSRF-TOKEN"))
const data = await fetch(process.env.API + "signUp", {
body: JSON.stringify({ email: email, password: password }),
mode: "cors", // no-cors, cors, *include
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "include", // include, *include, omit
headers: {
"content-type": "application/json",
"csrf-token": token,
},
method: "POST",
}).catch(error => this.setState({ isError: true }))
if (data.ok) {
console.log(data.ok)
data.json().then(content => {
console.log(content)
this.props.changeAuth(content.authStatus)
this.props.setCategories(content.user.categories)
this.props.getEvents(content.user.events)
resetForm()
})
} else {
this.setState({
isError: true,
})
}
}
The cookies will not work if the front-end and server domains are different.
You can set up a proxy through Netlify to redirect all requests to a path (eg. /api/
) of the front-end domain to the backend domain.
To set up the proxy, you can create a _redirects
file in the publish directory (eg. public
) with the following configuration:
/api/* https://<server-url>:<server-port>/:splat 200
Lastly, send all HTTP requests to that front-end path (eg. https://<front-end-url>/api/users
) instead of the backend URL to ensure that the cookies work correctly.
...when it is in production, the cookie always returns undefined...
One possible reason could be that you are using HTTPS in production (which should be the case) and the browser ignores the CSRF cookie because it is not set as a secure cookie. To fix this issue use secure cookies in production and non-secure ones in development.
P.S.
To make the first cookie to be secure replace this code
app.use(csurf({ cookie: true }));
with that:
app.use(csurf({
cookie: {
httpOnly: true,
secure: !devModeFlag
}
}));
The httpOnly
setting ensures JS on the client cannot touch this cookie which is good for extra security. The boolean devModeFlag
variable should be set to true in development and to false in production.
To make the second cookie to be secure replace this code
res.cookie("XSRF-TOKEN", req.csrfToken());
with that:
res.cookie("XSRF-TOKEN", req.csrfToken(), { secure: !devModeFlag });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With