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