prevent monitoring app from creating session in expressJS/NodeJS - node.js

I am using a monitoring app which sends HTTP requests to my Node application (I can filter out these monitoring HTTP requests using the user agent via req.headers['user-agent']). I am using sessions in express as follows (using express-sessions):
app.use(express.session({ secret: 'adadafadgdgfgd34', cookie: {maxAge: new Date(Date.now() + 3600000)}, store: new (require('express-sessions'))({
storage: 'redis', instance: redisConnection, collection:'sessions', expire: 3600000 }) }));
Unless I am mistaken, the session & sessionID get generated as soon as the middleware gets hit. I am thinking of placing code before this middleware call to intercept/check the user agent and only call this session code if its a regular user agent (i.e. not coming from an automated monitor with a non-standard user agent) though I do not think I can use req.headers call there. Is there any other approach to better handle this scenario (or a module/library I can use)? The end goal is to avoid storing the monitoring server HTTP requests from generating a new session every time it hits my app and avoid storing it in redis (I would prefer not to change the monitoring server if possible).

Related

node.js express session access token doesn't persist after changes

I'm quite new to OAuth and not sure what to do with the access token I receive from another party. Right now I'm using express session on https with secure and httpOnly settings. This works fine, until I upload an image on the same API server (which happens after I add a product). Everytime my server detects changes, the token I saved becomes undefined, this means that the user has to go through the whole OAuth process again.
Since I use MYSQL, is it possible to save the token information in the database (expiry, refreshtoken, accesstoken) linked to the user or is there a better way to save this data?
My setup is very basic, I have one API Server and one React app for front-end.
I receive the token information by making an API call with my own API to the other party, the response from this party is what I end up sending as cookies to the React app.
This is the session code I have right now:
app.use(
session({
secret: process.env.SESSION_SECRET,
name: "token",
cookie: {
secure: true,
httpOnly: true,
},
resave: true,
saveUninitialized: true,
})
);
For anyone that runs into the same problem, by default express session uses MemoryStore. I missed this when I was reading the documentation.
MemoryStore is purposely not designed for a production environment. It
will leak memory under most conditions, does not scale past a single
process, and is meant for debugging and developing.
To fix this, you can use either "cookie-session" or pick one of the stores from the documentation.
https://www.npmjs.com/package/express-session#compatible-session-stores

passport-local cookie and session database handling

I am currently trying to setup an oauth service for my current project. Being new to all this, I have read many articles on the subject, but I am still missing some pieces.
My environment is:
servers: node, express, passport
authentication: local strategy (using my own accounts / passwords)
I have a database with user/password ( passwords run through bcrypt)
web interface: react, server accesses through superagent
On my nodejs server, I am using these modules:
express
express-session
express-mysql-session
passport
passport-local
bcrypt
Different parts of the solutions are working: I can create new users, create new sessions, see their content in the express-mysql-session database.
However I am quite confused on the following:
when my web client tries to access protected routes, I don't seem to be getting any cookie in the request. Is it that superagent will not send my passport cookie by default? I read somewhere that in single page apps, jwt might be more appropriate, is that linked to this problem?
despite all I read, I am still confused about deserializeUser. My understanding is that with the passport-local solution, upon access, the web client will send the session cookie, which contains the session Id. Passport will fetch further information concerning this session from database, and then continue to handle the request. Does this session info retrieval happen automatically (in express-mysql-session?)? Or do you have to "manually" do it in deserializeUser (many examples show a User.findById call in there)? If you have to do it "manually", it means that you have to access the express-mysql-session db using another connection than the one this module is using?
to log out, is req.logout() enough to ensure the session is erased from the session db entirely?
Answers I found so far:
One has to add the withCredential method to superagent, to get it to send authentication cookies:
res = await superagent
.get(url)
.withCredentials()
.send();
On the CORS side of things, on the server, the 'credentials' option is required if using the 'cors' npm module, for instance:
app.use(cors({
origin: ['http://localhost:3003'],
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
credentials: true,
}));
All session information is automatically retrieved by these modules. However, many example show this call going back to the user database to get more information (rights, other info). The goal is to avoid having the same information in two locations (sessions db, and user profiles db), and having these getting out of sync (when an account gets closed etc...)
req.logout() disconnects the session, but the session information sticks around in the database.
The following question put me on the right track: how to delete cookie on logout in express + passport js?. You need to use req.logout, res.session.destroy, and while you're at it res.clearCookie to delete the client cookie:
router.post('/logout/',
(req, res) => {
req.logout();
res.status(200).clearCookie('connect.sid', {
path: '/',
secure: false,
httpOnly: false,
domain: 'place.your.domain.name.here.com',
sameSite: true,
}).end();
req.session.destroy();
},
Session is disconnected, database cleaned, cookie gone.

Saving session data across multiple node instances

EDIT - Oct 22, 2017
There was more than one reason our sessions weren't persisting, I've had to change ourexpress-session options to this:
api.use(session({
secret: 'verysecretsecret',
resave: false,
saveUninitialized: false,
cookie: {
path: '/',
httpOnly: true,
domain: 'domain.dev',
maxAge: 1000 * 60 * 24
},
store: new MongoStore({ mongooseConnection: mongoose.connection, autoReconnect: true })
}));
Apparently domain: 'localhost' causes express-session to start a new session every single time someone starts a session and then refreshes/navigates away and back when you have a seperate node instance for session handling.
I've solved this issue by doing the following:
Added 127.0.0.1 domain.dev to my hosts file located in C:\Windows\System32\drivers\etc.
I needed a place to store sessions as per the answers given below, so we chose MongoDB. This meant I had to add the following to my express-session options:store: new MongoStore({ mongooseConnection: mongoose.connection, autoReconnect: true })
Added the httpOnly: true property to the express-session options.
Because we use jQuery for our ajax requests, I had to enable some settings in the front-end web app before making calls to the back-end:$.ajaxSetup({
xhrFields: { withCredentials: true },
crossDomain: true,
});
ORIGINAL POST
I'm currently working on a platform for which was decided to have the API running on port 3001 whilst the web application itself is running on port 3000. This decision was made to make monitoring traffic more easy.
Now, we're talking about express applications so we defaulted to using the express-session npm package. I was wondering if it's at all possible to save session data stored on the node instance running on port 3001 and be retrieved by the node instance running on port 3000 if that makes sense.
To elaborate, this is our user authentication flow:
User navigates to our web app running on port 3000.
User then signs in which sends a POST request to the API running on port 3001, session data is stored when the credentials are correct, response is sent so the web app knows the user is authenticated.
User refreshes the page or comes back to the web app after closing their browser so web app loses authenticated state. So on load it always sends a GET request to the API on port 3001 to check if there's session data available, which would mean that user is logged in, so we can let the web app know user is authenticated.
(If my train of thought is at fault here, please let me know)
The problem is that express-session doesn't seem to be working when doing this.
I've enabled CORS so the web app is able to send requests to the API. And this is what the express-session configuration looks like:
api.use(session({
secret: 'verysecretsecret',
resave: false,
saveUninitialized: false,
cookie: {
path: '/',
domain: 'localhost',
maxAge: 1000 * 60 * 24
}
}));
Preferably help me solve this problem without using something like Redis, I'd simply like to know if solving this problem is possible using just express-session and node.
Preferably help me solve this problem without using something like Redis
You want us to help you solve this problem preferably without using the right tool for the job.
Without Redis you will need to use some other database. Without "something like Redis" (i.e. without a database) you will need to implement some other way to handle something that is a book example use case for a database.
And if you're going to use a database then using a database like Redis or Memcached is most reasonable for the sort of things where you need fast access to the data on pretty much every request. If you use a slower database than that, your application's performance will suffer tremendously.
I'd simply like to know if solving this problem is possible using just express-session and node.
Yes. Especially when you use express-session with Redis, as is advised in the documentation of express-session module:
https://github.com/expressjs/session#session-store-implementation
If all of your instances work on the same machine then you may be able to use a database like SQLite that stores the data in the filesystem, but even when all of your instances are on the same box, my advice would be still to use Redis as it will be much simpler and more performant, and in the case when you need to scale out it will be very easy to do.
Also if all of your session data can fit in a cookie without problems, then you can use this module:
https://www.npmjs.com/package/cookie-session
that would store all of the session data in a cookie. (Thanks to Robert Klep for pointing it out in the comments.)

Working with Sessions in Express.js

I need help understanding the concept of sessions for a web application. I am running a Node.js server with Express 3.0.
My goals are to:
Create a session for each user that logs in
Store this session and use it for validating if the user is already logged in (prevent two devices using the same user at the same time) and to limit access to certain pages (by matching session ID to some other data)
I will be using MemoryStore to save the sessions (seems easiest). If the above goals make sense can you provide a thorough explanation of how to achieve them?
Express has nice examples in the github repo. One of them deals with authentication and shows how to attach the user to the req.session object. This is done inside the app.post('/login') route.
To limit access to certain pages add a simple middleware to those routes
function restrict(req, res, next) {
if (req.session.user) {
next();
} else {
req.session.error = 'Access denied!';
res.redirect('/login');
}
}
app.get('/restricted', restrict, function(req, res){
res.send('Wahoo! restricted area, click to logout');
});
As Brandon already mentioned you shouldn't use the MemoryStore in production. Redis is a good alternative. Use connect-redis to access the db. An example config looks like this
var RedisStore = require('connect-redis')(express);
// add this to your app.configure
app.use(express.session({
secret: "kqsdjfmlksdhfhzirzeoibrzecrbzuzefcuercazeafxzeokwdfzeijfxcerig",
store: new RedisStore({ host: 'localhost', port: 3000, client: redis })
}));
Use MemoryStore in express ONLY if you are not creating multiple instances (such as with the cluster module). If you are load balancing across machines, your load balancer will need to use sticky / persistent sessions.
If you meet those requirements, then all you need to do is upon login, once the credentials are validated, set a session variable to indicate logged in, for example:
req.session.loggedIn = true;
If you want to check if a user is logged in, simply check that variable.
if (req.session.loggedIn) {
// user is logged in.
}
else {
// user is not logged in.
}
You mentioned preventing a single user from having sessions more than one session at a time. To achieve that, you may need to store something in a database indicating that the user is logged in. I warn you, this can be dangerous because of stale sessions. For example, what if a user logs in, but never logs out? What if they close their browser window so the session is gone forever?
Express has no concept of an idle session expiration. I have implemented such a thing by storing all sessions in the database along with a last accessed timestamp and then periodically clean up the session data based on the time. But then you need to update your list of who is logged in as well.

Mashery IODocs - Can it support passportjs authentication from my REST API?

I am using iodocs from Mashery to be the developer front end to my REST API. My API is written with Node / Express, and uses PassportJS to authenticate the user (local strategy). My implementation requires the user to use the /login endpoint, passing in username and password. Then, Passport serializes the user in a cookie, so that subsequent requests do not need to log in.
When using iodocs, the cookie that Passport sets ("connect.sid") is not passed back in subsequent requests.
Is there a way to do this? Is there an authentication method that IODocs supports that works this way?
Cookies WILL traverse across the ports. An issue you may be encountering is that "connect.sid" is also being set by I/O Docs in that it's using the Express session.js middleware module, so that cookie value is probably getting overwritten.
Try updating I/O Docs app.js with a different cookie name in the session initializer -- setting the "key" value:
app.use(express.session({
secret: config.sessionSecret,
key: 'iodocs.connect.sid',
store: new RedisStore({
'host': config.redis.host,
'port': config.redis.port,
'pass': config.redis.password,
'maxAge': 1209600000
})
}));

Resources