I have 2 servers. One hosting a next.js application on localhost:5555 and another hosting an express server for the api on localhost:4444.
The authentication api returns a cookie however this is not being set in the browser running on localhost:5555.
res.cookie('cokkieName', 'bob', {
domain: '127.0.0.1:5555',
maxAge: 900000,
httpOnly: true,
});
res.status(200).json({
session: jwtSigned,
role: 'default',
});
My cors setup is:
const options: cors.CorsOptions = {
allowedHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'X-Access-Token', 'Authorization'],
credentials: true,
methods: 'GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE',
origin: 'http://localhost:5555',
preflightContinue: false,
};
I'd prefer to set the cookie with the api rather than on the via next.js.
I've tried alternating the cors settings but have had no success. The client call uses axios and has withCredentials
set.
I have 2 servers. One hosting a next.js application on localhost:5555 and another hosting an express server for the api on localhost:4444.
The authentication api returns a cookie however this is not being set in the browser running on localhost:5555.
res.cookie('cokkieName', 'bob', {
domain: '127.0.0.1:5555',
maxAge: 900000,
httpOnly: true,
});
res.status(200).json({
session: jwtSigned,
role: 'default',
});
My cors setup is:
const options: cors.CorsOptions = {
allowedHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'X-Access-Token', 'Authorization'],
credentials: true,
methods: 'GET,HEAD,OPTIONS,PUT,PATCH,POST,DELETE',
origin: 'http://localhost:5555',
preflightContinue: false,
};
I'd prefer to set the cookie with the api rather than on the via next.js.
I've tried alternating the cors settings but have had no success. The client call uses axios and has withCredentials
set.
5 Answers
Reset to default 11 +100As mentioned in the other answer, you cannot set cookie on a different domain, as it can be a security risk.
But from your question...
Since the API server is running the authentication logic, the cookie should be set on the API server domain, NOT on client domain
So in your API server, you can change code to
res.cookie('cokkieName', 'bob', {
domain: '127.0.0.1:4444',
maxAge: 900000,
httpOnly: true,
});
or just remove the domain as it defaults to server domain.
res.cookie('cokkieName', 'bob', {
maxAge: 900000,
httpOnly: true,
});
As you have a client running on localhost:5555 and API server running on localhost:4444, it's a cross domain call and you need to pass withCredentials
option to axios
to pass the cookie. You might not be able to see the cookie in browser, as it can be a security risk, but can echo the cookie value on server console.
axios.get("url",{
withCredentials: true
}).then(res => {
console.log(res);
});
Note that credentials option work only if Access-Control-Allow-Origin header is not a wildcard like *
I have a created a server to mimic the code you posted. The server basically echoes the request cookie back.
It can be accessed through this stackblitz. You can toggle the withCredentials flag to see the difference in response
You can not set a cookie for other domains, Because that would be an enormous security risk.
But if you are looking for a way to share a cookie between your applications, the only way you have is to share a single domain between these applications, either separate them by url prefix or create one of them as a subdomain of other.
domain.com
and api.domain.com
. This way by specifying domain.com
in your cookie both domains have access to it
If you concern about developing environment of yours, you can use Nginx
and proxy_pass
these two applications in single domain
What you are trying to do is definitely possible, the problem is that cookies are not port specific: Are HTTP cookies port specific? so you should specify a domain name but no port.
If you set a Cookie on its own domain (or if you don't specify it at all), all of the subsequent requests will carry the cookie even if initiated by a different port (only when withCredentials
set to true
, though). Also, note that, if set, the domain of the Cookie must match exactly the current domain, or parent domain (localhost
is considered as different than 127.0.0.1
)
Note: doing so shows the following warning in Chrome:
A cookie associated with a cross-site resource at http://example.com/ was set without the
SameSite
attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set withSameSite=None
andSecure
. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.
So, prepare your code for the future web standards!
I had a similar problem with nuxt.js. To set cookies in your browser using express, I suggest using the library Cookies
In my case I set cookies in express like this:
setResponseCookie(req, res, token, strategyName) {
const isHttps = req.protocol === 'https';
let { cookie: { maxAge } } = config;
const cookieSettings = isHttps => ({
maxAge,
httpOnly: true,
secure: isHttps,
});
const cookies = new Cookies(req, res);
cookies.set('auth.strategy', strategyName, cookieSettings(isHttps))
cookies.set('auth.token', token, cookieSettings(isHttps))
}
and then in express route you can add to this like:
let token = sign(user);
this.setResponseCookie(req, res, token, 'local-login')
res.json({ token, status: 'success' });
or
let token = sign(req.user);
this.setResponseCookie(req, res, token, 'google')
res.redirect(config.client.url);
I managed to fix this issue of the server not setting the browser cookies by setting my res.cookie like the following
res.cookie("Test", "If you see this then it works", {httpOnly: false, secure: true, sameSite: "none"}).json({message: "Finished"});
I checked to see if the cookie was set by opening up the chrome dev tools, going to the application, then cookies, then the site URL.
There are a few things I would like to preference about the above code. This method only works on a URL starting with https, this means it will not fix the problem if on a URL like http://localhost:3000. I don't know if it matters if httpOnly is true or false. Also, What is necessary when setting the cookie this way is to pass in the properties secure: true, sameSite: "none"
. Passing these values into res.cookie was what finial fixed the issue for me. I have not seen this solution posted anywhere and decided I out to let you know.
My code that fetches the data from the API and triggers the server to set the browser cookie looks like this for anyone who is interested.
fetch(domain.com/testSetCookie', {
method: 'get',
mode: "cors",
credentials: 'include' //<-- important
})
.then((res) => {
//console.log(res);
return; //res.json();
})
.then((data) => {
console.log(data);));
})
.catch((err) => {
console.log("an error happeoned");
console.log(err);
});
localhost
interacts poorly with storage, cookies, CORS, also in browser-dependent ways as I remember. Fake some127.0.0.1
domains viahosts
or something. Edit: it seems to be "wontfix" in case of Chrome for example (stackoverflow.com/questions/10883211/…) – tevemadar Commented Oct 27, 2019 at 14:47