I have problem with setting a cookies via express. I\'m using Este.js dev stack
and I try to set a cookie in API auth /login
route. Here is the cod
Struggling with this for a 3h, and finally realized, with axios
, I should set withCredentials
to true
, even though I am only receiving cookies.
axios.defaults.withCredentials = true;
There's a few issues:
httpOnly : false
will not be accessible through document.cookie
in the browser. It will still be sent with HTTP requests, and if you check your browsers' dev tools you will most likely find the cookie there (in Chrome they can be found in the Resources tab of the dev tools);next()
that you're calling should only be used if you want to defer sending back a response to some other part of your application, which—judging by your code—is not what you want.So, it seems to me that this should solve your problems:
res.cookie('token', jwt.token, {
expires : new Date(Date.now() + 9999999),
httpOnly : false
});
res.status(200).send({ user, token: jwt.token });
As a side note: there's a reason for httpOnly
defaulting to true
(to prevent malicious XSS scripts from accessing session cookies and the like). If you don't have a very good reason to be able to access the cookie through client-side JS, don't set it to false
.
Double check the size of your cookie.
For me, the way I was generating an auth token to store in my cookie, was causing the size of the cookie to increase with subsequent login attempts, eventually causing the browser to not set the cookie because it's too big.
Browser cookie size cheat sheet
A cookie can't be set if the client and server are on different domains. Different sub-domains is doable but not different domains and not different ports.
If using Angular as your frontend you can simply send all requests to the same domain as your Angular app (so the app is sending all API requests to itself) and stick an /api/ in every HTTP API request URL - usually configured in your environment.ts file:
export const environment = {
production: false,
httpPhp: 'http://localhost:4200/api'
}
Then all HTTP requests will use environment.httpPhp + '/rest/of/path'
Then you can proxy those requests by creating proxy.conf.json as follows:
{
"/api/*": {
"target": "http://localhost:5200",
"secure": false,
"changeOrigin": true,
"pathRewrite": {
"^/api": ""
}
}
}
Then add this to ng serve:
ng serve -o --proxy-config proxy.conf.json
Then restart your app and it should all work, assuming that your server is actually using Set-Cookie in the HTTP response headers. (Note, on a diff domain you won't even see the Set-Cookie response header, even if the server is configured correctly).
I had the same issue. The server response comes with cookie set:
Set-Cookie:my_cookie=HelloWorld; Path=/; Expires=Wed, 15 Mar 2017 15:59:59 GMT
But the cookie was not saved by a browser.
This is how I solved it.
I use fetch
in a client-side code. If you do not specify credentials: 'include'
in fetch
options, cookies are neither sent to server nor saved by a browser, although the server response sets cookies.
Example:
var headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
return fetch('/your/server_endpoint', {
method: 'POST',
mode: 'same-origin',
redirect: 'follow',
credentials: 'include', // Don't forget to specify this if you need cookies
headers: headers,
body: JSON.stringify({
first_name: 'John',
last_name: 'Doe'
})
})
Hope it helps somebody.
There is no problem to set "httpOnly" to true in a cookie.
I am using "request-promise" for requests and the client is a "React" app, but the technology doesn't matter. The request is:
var options = {
uri: 'http://localhost:4000/some-route',
method: 'POST',
withCredentials: true
}
request(options)
.then(function (response) {
console.log(response)
})
.catch(function (err) {
console.log(err)
});
The response on the node.js (express) server is:
var token=JSON.stringify({
"token":"some token content"
});
res.header('Access-Control-Allow-Origin', "http://127.0.0.1:3000");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header( 'Access-Control-Allow-Credentials',true);
var date = new Date();
var tokenExpire = date.setTime(date.getTime() + (360 * 1000));
res.status(201)
.cookie('token', token, { maxAge: tokenExpire, httpOnly: true })
.send();
The client make a request, the server set the cookie , the browser (client) receive it (you can see it in "Application tab on the dev tools") and then I again launch a request to the server and the cookie is located in the request: "req.headers.cookie" so accessible by the server for verifying.