Reputation: 15374
Just getting started with Passport JS and so far can have a user login, restrict pages depending on user role etc. One things I have noticed though is I can login via multiple devices using the same credentials. This is of concern to me as the same login can be shared which is not something I want.
My questions is how can i stop this? My current set up is just an express app running on a single instance (not got as far as load balancing across multiple instances as not required as yet, when I do, I understand I can use redis to store session details and go from there ). But for now I was wondering if there was a way I can do this with Passport with my current setup
I have omitted the parts I feel are unnecessary so not to bloat out the question
# server.js
const session = require('express-session');
const passport = require('passport');
require('./config/passport')(passport); // pass passport for configuration
// Init App
const app = express();
// BodyParser Middleware
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json()); // get information from html forms
app.use(session({
secret: 'cookie_secret',
name: 'cookie_name',
resave: true,
saveUninitialized: true,
cookie: { maxAge: 5400000 },
}));
app.use(passport.initialize());
app.use(passport.session());
// routes
require('./routes/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport
const port = process.env.PORT || 4000;
app.listen(port, () => {
console.log('listening on port ' + port);
});
Passport configuration
# config/passport.js
const bcrypt = require('bcryptjs'); // hash password
const LocalStrategy = require('passport-local').Strategy;
const pool = require('./database.js'); // database Connection
const User = require('../models/user'); // load up the user model
module.exports = (passport) => {
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
passport.use('local-login', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true,
},
(req, email, password, done) => {
pool.query('SELECT * from users where email=$1', [email], (err, result) => {
if (err) return done(null, false, { message: 'Incorrect Email or Password' });
if (result.rows.length > 0) {
const user = result.rows[0];
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch === true) { return done(null, user);
}
return done(null, false, { message: 'Incorrect Email or Password'
});
}); // bcrypt
} else {
return done(null, false, { message: 'Incorrect Email or Password' });
}
});
}));
};
When a user logs in they go through the local-login strategy and I have methods then to check whether a user can access the page trying to be accessed
const checkAuthentication = require('../helpers/auth');
// Members Page
app.get('/members', checkAuthentication, async (req, res) => {
res.render('members');
});
app.post('/login', passport.authenticate('local-login', {
successRedirect: '/members',
failureRedirect: '/',
failureFlash: true,
}));
# auth.js
module.exports = (req, res, next) => {
if (req.isAuthenticated()) {
if (new Date(req.user.membership_expiry_date) < new Date()) { // if expiry date < todays date
req.flash('error', 'Membership Expired');
return res.redirect('/');
}
} else {
return res.redirect('/');
}
return next();
};
I hope there is enough information there, to recap, I am looking to only allow a user to login one session at a time, so one device at a time, no multiple logins
Thanks
Upvotes: 0
Views: 3978
Reputation: 76414
If you want to support a single session for a user, then you will need to make sure that:
In general, sessions are not alive forever, but if you absolutely need to have a single login session, then whenever the session expires, your users will not be able to create a new session.
You will need an is_logged_in or similar field in the database which will be changed whenever a user logs in or out. If a user tries to log in, you try his or her login status and deny logging in if the user is already logged in.
Cases like the user buying a new device and not logging out from the previous device, or a device getting broken is handled nicely. You can, for instance allow a login after 24 hours of a session being used, but in that case you will need to automatically log out the user from the old session, if he or she attempts to do something with the old session.
In fact, you can achieve what you intend to achieve in many ways, but you will need to keep attention to the details.
Upvotes: 2