I'm using NodeJS + express + express-session to persist a userID from anywhere in the application.
On the first route, my session is defined
userProfileRoutes.route('/authentication').post((req, res) => {
req.session.userID = 10; //example
console.log(req.session)
}
The result of the console.log is:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true,
secure: true },
userID: 10 } // this is the right value
But then, from a different route, I can't see the value:
userProfileRoutes.route('/edit').get(function (req, res) {
console.log('After the nav edit route');
console.log(req.session);
}
And this prints
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true,
secure: true }
} // ID VARIABLE DISAPEARS HERE
I am configuring express-session using these parameters:
app.use(session({
secret: 'secret',
proxy: true,
resave: false,
saveUninitialized: true,
withCredentials: true,
cookie: { secure: true },
store: new MongoStore({ mongooseConnection: db })
}));
Why is my userID not persisted between requests and on all routes?
You are setting cookie: {secure: true} but trying to access your server using HTTP.
From the express-session documentation:
cookie.secure
Note be careful when setting this to true, as compliant clients will not send the cookie back to the server in the future if the browser does not have an HTTPS connection.
Please note that secure: true is a recommended option. However, it requires an https-enabled website, i.e., HTTPS is necessary for secure cookies. If secure is set, and you access your site over HTTP, the cookie will not be set.
Make sure you are either using HTTPS (always in production!) or you set cookie.secure to false (maybe, and for development only!)
The secure flag in cookies
The secure flag is an option that can be set by the application server when sending a new cookie to the user within an HTTP Response. The purpose of the secure flag is to prevent cookies from being observed by unauthorized parties due to the transmission of a the cookie in clear text.
To accomplish this goal, browsers which support the secure flag will only send cookies with the secure flag when the request is going to a HTTPS page. Said in another way, the browser will not send a cookie with the secure flag set over an unencrypted HTTP request.
By setting the secure flag, the browser will prevent the transmission of a cookie over an unencrypted channel.
from https://www.owasp.org/index.php/SecureFlag
Cookies in express-session
Following common practice, express-session uses cookies to store a session ID and server side storage (mongoDB in your case) to store session data. If the browser does not send your session ID because it can't find a valid cookie, your server will assume there is no session, and save the user id on a new session on every request.
When you got to /authentication it will save the ID on a new session. When you try to read in in a different request, the session ID has changed and you have no value in userID.
Related
New sessions are created for every page, I'm setting the token in the server-side session after the login. But it is not available on the next page a new session is created on that page How to avoid this? The database disconnects after the API call on the page. Every time the database connection is made before calling the API. I require the token throughout the login.
src/index.ts
...
const session = require("express-session")
app.use(session({
name : 'Server_ID',
secret : 'something',
resave:false,
saveUninitialized: true,
rolling: false,
cookie: {
same site: true,
// secure: "development",
httpOnly: true,
secure: false,
maxAge:1000 * 60 * 10
}
}));
...
you must set credentials flag with your API call
for example if you're using axios, you do this:
axios.defaults.withCredentials = true;
with this approach cookie is going to sent along with your request call
make sure you have correct CORS settings too.
I have an application with express-session#1.17.1, passport#0.3.2, and connect-redis#4.0.0. We manage the session of a user with cookies. There is a scenario when we need to let the user go to a different domain to complete the checkout process and come back. If we keep the cookie property to be strict the user's session is lost as it is unable to find the cookie, coming from 3rd party. A new session will be generated for the given user. There can be one way to handle this situation, which I can think of.
Here is my session configuration
return session({
genid: () => uuidv4(),
store: redisStore,
cookie: {
maxAge: 30 * 24 * 60 * 60 * 1000 /* 30 days */,
secure: true,
sameSite: true,
path: '/',
},
secure: true,
sameSite: true,
secret: '123',
resave: false,
name: 'prod',
saveUninitialized: true,
})(req, res, next);
Notice, I want to keep the same site property true, to prevent any CSRF. This also prevents cookies from valid third-party domains redirecting to our site.
Reattach the existing session with the session to a user coming back to the callback URL. This will require passing a session identifying token in the callback URL.
If I just pass the session-id in the callback URL, I can see the last session of the user and able to attach the. I don't think passing session-id is safe enough. I would like to know the correct way to do this.
My attempts at logging in are not getting saved to express session in production. I am saving the session in Mongo Store and the sessions are coming up in MongoAtlas as modified (they way they should appear), but for some reason the server is not recognizing that there is an existing session and is making a new one. When I enable express-session debug, it logs express-session no SID sent, generating session on each request to the server. This makes me think that the session id isn't getting sent with the request and that the problem has something to do with my client and server being on different domains (my client address is https://example.com and my server is on https://app.example.com. I originally had my client on https://www.example.com but changed it thinking that the cookie was getting mistaken for a 3rd party cookie (maybe it still is).
My client is hosted on Firebase Hosting and my Express server is hosted on Google Cloud Run
my express-session settings
app.set('trust proxy', true)
app.use(session({
secret: 'myappisasecret',
resave: false,
saveUninitialized: false,
secure: true,
store: new MongoStore({mongooseConnection: mongoose.connection}),
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 7, // 1 week
sameSite: 'lax',
secure: true,
domain: 'mysite.com'
},
proxy: true // I think this makes the trust proxy be useless
}))
Below is my coors server stuff. This code is located above the code above, but I don't think it is causing any issues, but think that it might be important to include.
let whitelist = ['https://app.example.com', 'https://www.example.com', 'https://example.web.app', 'https://example.com']
let corsOptions = {
origin: (origin, callback) => {
if (whitelist.indexOf(origin) !== -1 || origin === undefined) {
callback(null, true)
} else {
console.log('Request Origin blocked: ', origin)
callback(new Error('Request blocked by CORS'))
}
},
credentials: true
}
app.use(cookieParser('myappisasecret'))
app.use(cors(corsOptions))
Since the server wasn't receiving a session id, I thought that maybe my client wasn't sending one so I added credentials: 'include' to my client request code
const reqHeaders = {
headers: {
"Content-Type": "application/json"
},
credentials: 'include' as any,
method: "GET"
}
fetch('https://app.example.com/u/loggedIn', reqHeaders)
.then(res => etc...
When this request gets submitted expression-session debug logs:
express-session saving z3ndMizKoxivXR0N9LBZYkPhDG65uvF2 and then
express-session split response
This makes me think that as it tries to save my user data to the session, it gets overwritten at the same time with an initial session data. I have set resave: false. But even then I still get express-session no SID sent with every request sent to the server.
Apparently when hosting with Firebase and Cloud Run cookie headers get stripped due to Google's CDN cache behavior.
Here's the documentation that describes that:
https://firebase.google.com/docs/hosting/manage-cache#using_cookies
I have no clue how to implement sessions now. F
Full warning message:
A cookie associated with a resource at http://127.0.0.1/ was set with `SameSite=None` but without `Secure`. A future release of Chrome will only deliver cookies marked `SameSite=None` if they are also marked `Secure`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5633521622188032.
However, I set cookies like:
res.cookie("token", token, {httpOnly: true, sameSite: 'none', secure: true});
Is there a way to get rid of this warning?
For a cookie required in a third-party or cross-site context, you should set both SameSite=None and Secure, as you are doing in your original example.
First question though - do you definitely need this cookie to be cross-site? In other words, are you expecting different sites to make a request to yours that require this cookie to be sent? Examples here would be if your site is embedded within an iframe on another site, hosting images where you want to have cookies included, or creating a token for additional security on a cross-site form submission.
If not, consider SameSite=Lax for this cookie instead. That way it will only be sent for requests within your site.
However, when you are developing on 127.0.0.1 or localhost you generally do not have a certificate for a valid HTTPS connection. I would suggest, in Express, using app.get('env); to get your current environment ('development' or 'production') and then using that to choose if you set Secure or not.
For example:
const express = require('express');
const app = express();
if (app.get('env') !== 'development') {
// production settings, assume HTTPS
app.set('cookie config', { httpOnly: true, sameSite: 'lax', secure: true });
} else {
// development settings, no HTTPS
app.set('cookie config', { httpOnly: true, sameSite: 'lax' });
}
// Later on when setting a cookie within your route, middleware, etc.
res.cookie('token', token, app.get('cookie config'));
You could also set up multiple cookie configs if you have different use cases on your site.
if (app.get('env') !== 'development') {
// production settings, assume HTTPS
app.set('cookie config 1p', { httpOnly: true, sameSite: 'lax', secure: true });
app.set('cookie config 3p', { httpOnly: true, sameSite: 'none', secure: true });
} else {
// development settings, no HTTPS
app.set('cookie config 1p', { httpOnly: true, sameSite: 'lax' });
// Assumes that I'm hosting all my test sites under localhost,
// so the browser won't actually see them as 3p
app.set('cookie config 3p', { httpOnly: true, sameSite: 'lax' });
}
You could also look up creating a self-signed certificate for your localhost environment, but that can be a bit of a pain. If you're going to do that, the effort might be better put into using some kind of container or vm for development.
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.