Express doesn't set-cookie when SameSite is none - node.js

I have read a lot of other similar questions, but I couldn't solve the issue.
My setup is Node + Express + PassportJs and everything works in development, but I have problems on production.
With the following code, I see that the session cookie is sent back in the response, but I also get a message saying that it won't be applied as SameSite is lax (the default) and the response comes from another site (frontend and backend do not have the same origin).
app.use(
session({
secret: "foo",
resave: false,
saveUninitialized: false,
store: MongoStore.create({ mongoUrl: process.env.MONGO_DB_CONN_STRING! }),
cookie: { httpOnly: true }
})
);
So I changed it to this, so to specify SameSite and Secure in production, but at this point, no cookie is set anymore!
app.use(
session({
secret: "foo",
resave: false,
saveUninitialized: false,
store: MongoStore.create({ mongoUrl: process.env.MONGO_DB_CONN_STRING! }),
cookie: isProduction ? { httpOnly: true, sameSite: "none", secure: true } : {} // <-- only change
})
);
What could be the cause? I've tried to fix it by playing with CORS (no success) and other 100 things. Yet it seems some quirk I am missing.

depending on what service you use to deploy your API(netlify, render.com, heroku other...) you have to enable proxy
this.app.enable('trust proxy');
it fixed my issue

Related

Why can't I set cookies over HTTPS?

I have a server that has its own domain and is using HTTPS. I have a website that has its own domain and is using HTTPS as well.
On the home page of the website, there is a login button and a sign up button. Both buttons lead to forms to execute their respective tasks, and both forms send requests to the server that respond with cookies, or at least that's what they are supposed to do.
I am using express-session in combination with Redis to store the session ids. Here is the config for that (connectToRedis is simply a function that returns a RedisClient):
const RedisStore = connectRedis(session);
const redisClient = await connectToRedis();
app.use(
session({
store: new RedisStore({
client: redisClient,
}),
cookie: {
httpOnly: true,
secure: true,
sameSite: "lax",
maxAge: TEN_YEARS,
},
resave: false,
saveUninitialized: false,
secret: SECRET,
name: AUTH_COOKIE,
})
);
For some reason the cookies are not being sent in the requests. The Set-Cookie header isn't even showing up! I tried changing SameSite to none (article), but that didn't work.
This is my first time deploying a production website, so all this HTTPS stuff is kind of new to me.
Thanks for your help, and if you need any extra information, please let me know.
IMPORTANT UPDATE:
Finally, I have made some progress (all it took was a pizza break).
I added the path key to my cookie config and gave it the value of /. I also set proxy to true.
So now it looks like this:
const RedisStore = connectRedis(session);
const redisClient = await connectToRedis();
app.use(
session({
store: new RedisStore({
client: redisClient,
}),
cookie: {
httpOnly: true,
secure: true,
sameSite: "none",
maxAge: TEN_YEARS,
path: "/",
},
resave: false,
saveUninitialized: false,
secret: SECRET,
name: AUTH_COOKIE,
proxy: true,
})
);
With this, the cookie is finally appearing in the requests, but it isn't being set in the browser...

Frontend does not get the node-session from Heroku, even though the session is stored in the database

My problem is, that the Frontend doesn't receive the session. Even though the session is stored in my database its not working. This problem only occurs when I use Heroku, locally it's working. I tried already a lot of solutions but none of them were working for me.
I hosted my frontend on Netlify with React and all of my backend is on Heroku.
app.set("trust proxy", 1);
app.use(
session({
key: "userId",
proxy: true,
secret: "verySecureSecret123",
resave: false,
saveUninitialized: false,
store: sessionStore,
cookie: {
secure: true,
expires: 60 * 60 * 24 * 30,
},
})
);

Read express session cookie from React js app

I'm building a project with authentication. I'm using Node+React. I set an express session cookie on the back-end and I want a component in react to read that cookie to see if the user is authenticated or not. For some reason I can not access that cookie from the react(client-side)... Maybe someone could help out?
BACK:
app.use(session({
name: process.env.SESS_NAME,
resave: false,
saveUninitialized: false,
secret: process.env.SESS_SECRET,
cookie: {
maxAge: parseInt(process.env.SESS_LIFETIME),
sameSite: true, //strict,
secure: process.env.NODE_ENV === "production"
}
}))
FRONT:
import Cookies from "js-cookie";
...
console.log("cookie", Cookies.get("sid"));
I have a cookie named "sid" in this case and I can see it in my console in the browser... but when I try to access it its undefiend
thanks!
Your issue is that you have not set the httpOnly property on the cookie when configuring session. The default value is true which will prevent client browsers from reading the cookie.
Note be careful when setting this to true, as compliant clients will not allow client-side JavaScript to see the cookie in document.cookie.
app.use(session({
name: process.env.SESS_NAME,
resave: false,
saveUninitialized: false,
secret: process.env.SESS_SECRET,
cookie: {
maxAge: parseInt(process.env.SESS_LIFETIME),
sameSite: false, // this may need to be false is you are accessing from another React app
httpOnly: false, // this must be false if you want to access the cookie
secure: process.env.NODE_ENV === "production"
}
}))
See the cookie options in docs

express-session Changes the session when the browser is closed

Server on express (4.16.3), with it the express-session (1.15.6) module works.
Code:
// ...
app.use(session({
secret: 'mySecret',
resave: false,
saveUninitialized: true,
store: new MongoDBStore({
uri: 'my-url',
collection: 'sessions'
})
}))
// ...
The essence of the problem: I open the Yandex browser - assign a session, then close it and when I reopen it - a new session. The matter is that authorization is tied to sessions.
The problem is observed in the Yandex browser, microsoft EDGE and in all mobile browsers, while in chrome and opera works correctly.
Help solve the problem or maybe something can replace the module express-sessions
This is happening because your browser default expire the cookie when the browser is closed. In order to fix you can add cookie:{ maxAge: 60000} to your session.
app.use(session({
secret: 'mySecret',
resave: false,
cookie:{ maxAge: 60000},
saveUninitialized: true,
store: new MongoDBStore({
uri: 'my-url',
collection: 'sessions'
})
if you want to make the cookie to not expire, the best way is to set a large number.
// this will it expire in 200 years
cookie: { maxAge: 9000000000000}
or a very far future date in expire property.
// this will expire in year 9999
cookie: {expires: new Date(253402300000000)}

Can't make lusca CSRF work with https: 403 forbidden

This is driving me nuts. I have tried reading the lusca source code but found it hard to understand.
Checked several examples too, but since each config is different, and the only debugging output I have are two strings to compare, I'd better ask for some help!
Here's the code server side:
app.use([
cookieParser(process.env.SESSION_SECRET),
session({
resave: false,
saveUninitialized: true,
secret: process.env.SESSION_SECRET,
store: new MongoStore({ url: MONGO_URL, autoReconnect: true }),
cookie: {
secure: process.env.NODE_ENV === 'production'
},
}), lusca({
csrf: true,
xframe: 'SAMEORIGIN',
xssProtection: true,
})]);
And from the clientside, I send Ajax POST requests with the x-csrf-token:l0gH3xmssge53E/p2NsJ4dGnHaSLdPeZ+bEWs= header in it:
fetch(url, {
method: 'POST',
credentials: 'include',
headers: {
'x-csrf-token': CSRF_TOKEN
}
});
Crazy thing is, it's working locally, but as soon as I go https in production, I get the 403 Forbidden error message.
Here are the versions I use:
"cookie-parser": "1.4.3",
"express-session": "1.15.3",
"lusca": "1.5.1",
Also I read this from the express/session doc:
Note Since version 1.5.0, the cookie-parser middleware no longer needs to be used for this module to work.
But as far as I'm concerned, I need to store some persistent ID of the users (longer than the session). I need to use cookies for that, right?
I'd like to understand better on the whole session/cookie thing, but until now I never found any useful resource on the topic.
Thanks!
If you are running your Node.js server behind a proxy you will need to set trust proxy to true:
var isProductionEnv = process.env.NODE_ENV === 'production';
app.use([
cookieParser(process.env.SESSION_SECRET),
session({
resave: false,
saveUninitialized: true,
secret: process.env.SESSION_SECRET,
store: new MongoStore({ url: MONGO_URL, autoReconnect: true }),
proxy: isProductionEnv,
cookie: {
secure:isPrudictionEnv,
},
}), lusca({
csrf: true,
xframe: 'SAMEORIGIN',
xssProtection: true,
})]);
app.set('trust proxy', isProductionEnv);
Check out this stack overflow answer. Also check out this page on Express behind proxies.

Resources