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
Related
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
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...
Express-Session is working in development environment, as it sets the "connect.sid" cookie in my browser. However, in production it's not storing the cookie, and instead of using the same session - it creates a new one every time. I believe that the issue would be fixed if I can somehow save third party cookies, as my app was deployed using Heroku. Lastly, I have also used express-cors to avoid the CORS issue (don't know if this has anything to do with the cookie issue). I have set {credentials: true} in cors, {withCredentials: true} in Axios, as well.
Heroku uses reverse proxy. It offers https endpoints but then forwards unencrypted traffic to the website.
Try something like
app.enable('trust proxy')
And check out
https://expressjs.com/en/guide/behind-proxies.html
Issue Solved! -> Add sameSite: 'none'
Full Cookie config (express-session) for production:
cookie: {
httpOnly: true,
secure: true,
maxAge: 1000 * 60 * 60 * 48,
sameSite: 'none'
}
Adding a "name" attribute to the session config worked for me:
{
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
proxy: true, // Required for Heroku & Digital Ocean (regarding X-Forwarded-For)
name: 'MyCoolWebAppCookieName', // This needs to be unique per-host.
cookie: {
secure: true, // required for cookies to work on HTTPS
httpOnly: false,
sameSite: 'none'
}
}
I've deployed my front-end(React with Typescript) and backend (Nodejs with Typescript) to Heroku separately. I'm using express-session and connect-redis to maintain the user session, for which I'm using an external Redis service called Redistogo.
When I run my apps locally, req.session is present, and upon authenticating the user during login, I'm able to set the user's session in req.session.user. However, when running the backend on Heroku in the exact same way, req.session remains undefined.
if(process.env.NODE_ENV === 'production') {
this.app.set('trust proxy', 1)
const rtg = url.parse(process.env.REDISTOGO_URL);
const redisClient = redis.createClient(rtg.port, rtg.hostname);
redisClient.auth(rtg.auth.split(":")[1]);
this.app.use(session({
name: 'random_pur',
store: new RedisStore({
redisClient
}),
secret: 'meow',
resave: true,
saveUninitialized: false,
cookie: {
secure: false,
sameSite: false,
maxAge: 36000000,
httpOnly: false,
}
}));
}
So I'm not sure what I'm missing here - it doesn't give any problems on localhost. Any help would be appreciated. Thanks!
I am developing an application in Angular 5 which connects to my server in NodeJS.
I add the user in the req.session object after the user is logged in.
When I make a second request from Angular 5 app to retrieve something from the API, the req.session doesn't contain the user and the req.sessionId is always different.
How can I make the session persistent?
Here are the session and cookieParser middlewares:
app.use(cookieParser(config.secret))
app.use(session({
secret: config.secret,
saveUninitialized: false,
resave: false,
cookie: {
httpOnly: true,
secure: false
}
}))
It was a CORS related issue. I needed to include { withCredentials: true } in the http request options in Angular.