Limit session per User - ExpressJS - node.js

I am using mongoDB as a Database and passportjs for authentication.
I want to have a maximum of 3 sessions per user.
For that, I created a sessionCount field on my mongo document.
everytime the user logs in, I increment the count and when they log out, I decrement.
But the problem arises when the session auto-expires. The session count stays the same.
Q. is there any way to "detect" session expiration so that I could decrement that sessionCount field ?

I am also facing the same kind of issue I solve it by using this way->
app.use(function(req, res, next) {
//Checking previously set cookie (if there is one)
var session = JSON.parse(req.cookies['session'] || '');
if (session && new Date(session.expires) < new Date()) {
// decrement the count
}
next();
});

Related

How do I reset a cookie connected to an already existing express session?

I am using Mongo Sessions to save user data (no logins). When a user logs in for the first time I create a new session on the db and set a cookie on the client, the session information is now saved in localStorage. I want to use the same session per user, so their session object also stores things like 'Last Seen' , 'Socket ID' etc. Things that change often.
The problem is when a user client deletes the cookie (by any chance) a new cookie + session is created so all that data is 'gone'.
My question is 'How do I set a new cookie that points to an already existing session'?
// Anytime there is a (re)connection save the socketID to the session
io.on('connection', function(socket: i.Socket) {
socket.handshake.session!.lastSeen = new Date();
socket.handshake.session!.socketID = socket.id;
socket.handshake.session!.save((err: Error) => {
if (err) {
console.log('Error in saving session! => ', err);
}
});
// Now each session has it's socketID!
});
Thank you for your help, I need this. You can also suggest other ways I can do this.
How about, checking existing session of the user, while creating a new session for an user? If exists, use that session id in the cookie.
But, lastSeen, socketID sounds like application logic. These can be maintained in database along with the user data.

Sliding expiration for access token with Loopback

I've installed Loopback and enabled ACL for a couple of models. I noticed that the Access Token is valid for ever, I would like to change this period somehow to, for example, an hour. But even better would be to reset this period when activity occurs (sliding expiration)
I've checked the documentation but couldn't fine anything about this subject. Any help/guidance would be appreciated!
When you call the login method you can specify a ttl property in seconds(I believe by default it's 2 weeks if you don't specify). Then you can have sliding expiration by having the following middleware:
app.use(loopback.token()); // You should have this already
app.use(function(req, res, next) {
// Make sure this middleware is registered after loopback.token
var token = req.accessToken;
if (!token) {
return next();
}
var now = new Date();
if ( now.getTime() - token.created.getTime() < 1000 ) {
return next();
}
req.accessToken.created = now;
req.accessToken.ttl = 604800; //one week
req.accessToken.save(next);
});

How to add current logged in status to Users Schema in MongoDB using Passport and node.js

I’m quite new to backend development…
With using my API I would like to be able to display a list of users and also indicate if they are currently logged in. I got the basic authentification working using passport and json web token
I’m not looking to get the current logged in user.
I want to be able to retrieve a list of users and see if they are logged in or not.
Like this:
var users = Users.find({});
// console.log(users) output:
{
name: 'foo'
password: ...
isLoggedIn: false
},
{
name: 'bar'
password: ...
isLoggedIn: true
},
{
name: 'baz'
password: ...
isLoggedIn: false
}
isLoggedIn would be set to true if the user is currently logged in and to falseif not.
How can I do that? Thank you!
It sounds like what you would like to do is update your MongoDB database based on login/logout events. To do this you could use something like mongoose to work with your Node backend to easily access your database in MongoDB.
You can include mongoose after installing with npm install mongoose like so:
var mongoose = require('mongoose');
var User = mongoose.model('User');
Note that User corresponds to whatever schema you create for storing user information.
Assuming you have some sort of router object for handling requests, you could construct route handlers for /logout and /login and use your imported mongoose User model to retrieve and then modify a specific User object as such:
// whenever user goes to '/login' (you can have, say, your 'login' button make a request to this URL
router.get('/login', function(req,res) {
// your authentication here; passport stores the currently authenticated user in req.user
var username = req.user.name; // here we assume the username is stored as 'name' as you have in your code but change this based on your schema
User.findOne({name: username}, function(err, user, data) {
if(err) res.send(err);
user.isLoggedIn = true;
user.save(function (err) {
if (err) {
console.log(err);
} else {
// redirect to some page here maybe
}
});
});
});
// whenever user goes to '/logout' (you can have a logout button make a request to this URL
router.get('/logout', function(req,res) {
// currently authenticated user is still in req.user
var username = req.user.name;
User.findOne({name: username}, function(err, user, data) {
if(err) res.send(err);
user.isLoggedIn = false;
user.save(function (err) {
if (err) {
console.log(err);
} else {
// redirect to login/register page maybe
}
});
});
});
So to summarize what this code would do:
based on the url a user would go to, our route handler would fetch one correct, unique User object from our database based on the name (username)
it would do so by accessing the username property of req.user which corresponds to the currently authenticated user with Passport, which, again will be different for all users
update the field that we use to keep track of login status (isLoggedIn)
and then save the changes, after which we are done updating the state to reflect whether the user is logged in or not, so we can now redirect to some other page or display other content
Finally then, you could retrieve a list of all users similarly to your code like so:
User.find({}, function(err, users, data) {
// all users from your database are in `users`
console.log(users);
});
Edit for expired sessions:
So, to track expired sessions, since you're using Passport, would in theory require functionality to signal with some sort of event / callback / message, etc. the moment the session is deemed invalid. Now that is tough to monitor and from my experience with Passport, stuff like that isn't implemented in all authentication strategies and might vary based on the strategy to be used by developers (think for instance if a browser window is closed, based on Passports authentication strategy, or just browser, it might destroy the cookie for the session right away and our server has no way of knowing about it). I do recommend checking out all the authentication strategies Passport offers in case there are some better ones here.
Now, if you would like to add functionality to track the users passive login/logout status with sessions yourself, you could use something related to cookies. Again, not necessarily one to use, but here's a couple handy Express modules: cookie-parser and cookie-session.
Then, you could set and read cookies like this, using cookie-parser:
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
app.use(cookieParser());
You would put this code somewhere right after the user is authenticated:
// cookies are stored here
console.log(req.cookies);
// configure your cookie
var options = {
expires: 1000 * 60 * 60, // expires after one hour
httpOnly: true
}
// Set cookie
res.cookie('session', ('user-' + req.user.name), options);
And then, on the client side check if that cookie is valid continuously on some time interval, and if it expired Date.now() > cookie.expires then make a GET request to /logout, and there log out the user (currently still authenticated) by updating MongoDB and all.
However, since this would require making a mechanism to basically simulate an expired session, I would recommend using something analogous to a timeout, which would be much easier to implement. Just a note, this is sort of analogous to mechanisms on some pages you might have encountered where you get a pop-up saying 'You will be logged out due to inactivity'. In your main.js or whatever client-side script define a function to keep going on a time-out, unless the user does some action.
var inactivity = function () {
var t;
// user doing something on your page, so keep resetting time counter when events happen
document.onmousemove = resetTimer;
document.onkeypress = resetTimer;
// this is a callback function that will get called once a time-out countdown is done
function timeOut() {
// make a request to '/logout' here and logout the current user (you still will have access to req.user from Passport)
// also can redirect from back-end route handler to the login page for instance
}
// this gets called whenever an event happens, resetting the counter of sorts
function resetTimer() {
t = 0;
t = setTimeout(timeOut, 1000 * 60 ) // set this to however long you should wait to log out your user time (in milliseconds)
}
};
So basically what this approach would let you do, is automatically invalidate sessions yourself, which means you would have much greater control over updating the state of your database and logging users out.
Hope this helps!

i want to connect db dynamically when user login nodejs mongodb

please help me to get out of it.
with login api i want to set mongoose connection string.
is it possible that with login api i set connection string and it works for other api also(which will call after login api)??
like first user login at that time i set db and for further api call works for that db?
i had tried
How to connect multiple mongodb database dynamically using mongoose?
but in this solution in each api they specifies requested param.
Can we set connection string of mongo by api call?
this is my app.js
var mongoose = require('mongoose');
var connections = [test = mongoose.createConnection('mongodb://localhost:27017/test'),
demo = mongoose.createConnection('mongodb://localhost:27017/demo')];
exports.getDatabaseConnection = function (dbName) {
if (connections[dbName]) {
//database connection already exist. Return connection object
return connections['dbName'];
} else {
connections[dbName] = mongoose.createConnection('mongodb://localhost:27017/' + dbName);
return connections['dbName'];
}
}
and this is my user.js
exports.authenticate = function (req, res) {
var db = app.getDatabaseConnection(req.body.keydb);
var userDetail = db.model('userdetail', UserSchema);
userDetail.findOne({
email: req.body.email,
userType: req.body.userType
}, function (err, user) {
if (err) throw err;
if (!user) {
res.send({ status: 400, message: req.i18n.__("user.authIncorrectEmail") });
} else {...}
});
}
when i am requesting keydb value as demo..then it should select demo string which is in connection array.
but it is not working.what i am doing wrong??
i am trying to connect mongoDb as per user login request comes. i want to set db once only not on each api call. if user logout then db should be disconnect and when again user login it should again connect.
You need to keep a list of database connections, one per session. One way to do it would be to have an object where the keys are the session IDs and the values are the database connections. Alternatively you can keep one connection per user ID if you want to allow a situation where the same user has multiple sessions and they each share the database connection. Now when you create a session on user login you also create a new database connection. When you destroy a session on user logout you have to close the connection and remove it from the object where it was stored. For all other endpoints except login and logout you just look up the connection and use it.
This is how you can achieve your goal but keep in mind that keeping a separate database connection per logged in user has many disadvantages and wastes a lot of resources, both in your application and in the database.
Can we set connection string of mongo by api call?
Yes, of course. But this will mean that people will be able to trick your app into e.g. breaking into other people's databases and you will be responsible for all of the legal consequences of that.

Expiring routes generation NodeJS

I want to setup temporary routes with a unique random string path on ExpressJS. These routes should be dynamically created and should give a 404 if somebody tries to use ie: http://example.com/login/a434bcd34d920bdfe 30 min after that uniqueid was created.
Any ideas on how to do that? I'm pretty new to NodeJS but judging from what I've seen there should be a library that does that :)
Something like this maybe?
// =========== app.js ============
app.get('/generate_url', function(req, res) {
// random string: "a434bcd34d920bdfe"
var extension = randomstring.generate();
var dynamicController = require('./login/'+extension);
dynamicController.init(app);
// Should expire in 20 minutes
dynamicController.expire(20*60)
res.status(200).send();
}
// =========== login.js ============
login.post('/login/:uniqueid', function(req, res) {
// uniqueid should match the extension generated before
var uniqueid = req.query.unique;
var username = req.body.username;
// Do something with this info
}
I think this is far from working fine but at least maybe somebody who's done
You asking two question:
How to create the route.
How to save the data about expired links.
1. How to create the Route:
You have to create a route that receieve the traffic from all the users, and check that the specific URL is valid.
First you create a route that get traffic from all the users.
app.get('/myroute/:id',function(){
/* This route will get any url that start with /myroute/
For example /myroute/abc
/myroute/def
*/
// req.params.id == what the URL is entered
if (is_expired(req.params.id)) return res.end('Sorry your link has expired')
res.send('Great you logged in!')
})
app.post('/login/:uniqueid', function(req, res) {
set_expire(req.params.uniquieid,30*1000*60) //30 minutes = 30*60*1000 miliseconds.
})
2. How to save the data about expired links.
How to implement set_expire and is_expired
You need to implement is using any kind of database. Redis is very good for that. I will show you example how to do it
using setTimeout. It will work. But if the server restart, all the users will be logged out.
users={}
function is_expired(uid){
return users[uid]
}
function set_expire(uid,time){
users[uid]=true
setTimeout(function(){
delete users[uid]
},time)
}
Usually this is done with a generic router handler that then consults some data store to see if the ID sent in the request is valid.
Also, note that a value that comes from a route specification like '/login/:uniqueid' is found in req.params.uniqueid, not in req.query.uniqueid. req.query is for actual query parameters (things after the ? in the URL).
var validIds = {};
// =========== app.js ============
app.get('/generate_url', function(req, res) {
// generate random string: "a434bcd34d920bdfe"
// make sure it's not already in use
var extension;
do {
extension = randomstring.generate();
} while (validIds[extension]);
// save this random string as a valid ID and store the expiration time
validIds[extension] = {expiration: Date.now() + 20 * 60 * 1000};
// return the random value to the caller
res.json(extension);
}
app.post('/login/:uniqueid', function(req, res) {
// uniqueid should match the extension generated before
var uniqueid = req.params.unique;
var username = req.body.username;
// now see if the id is still valid
var idInfo = validIds[uniqueid];
if (!idInfo || idInfo.expiration < Date.now()) {
return res.sendStatus(404);
}
// id is valid, do something with this info
}
Then, you can have some interval timer that regular cleans up any expired ids. This is just housekeeping to keep the validIds object from growing forever - the actual expiration value is still checked before validating the id.
// clean up expired validIds object every 10 minutes
setInterval(function() {
var now = Date.now();
for (var id in validIds) {
if (validIds[id].expiration < now) {
delete validIds[id];
}
}
}, 10 * 60 * 1000);
Note: Since you probably want your uniqueIDs to survive a server restart, you need to regularly persist them to some sort of backing store (database, flat file, etc...) and then you need to read that data in upon server startup.
If you want any sort of user activity to "renew" the timeout on the uniqueid, you can install some middleware that resets the time at an appropriate access.

Resources