Richlewis
Richlewis

Reputation: 15374

One session per user - passport JS

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

Answers (1)

Lajos Arpad
Lajos Arpad

Reputation: 76414

If you want to support a single session for a user, then you will need to make sure that:

Any session will be alive forever, or until logout

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 store user login data in the database

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.

You make sure that extreme cases are handled well

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

Related Questions