使用Passport进行身份验证后创建新会话

时间:2014-03-05 21:10:46

标签: node.js session authentication passport.js

我用护照创建了一个简单的身份验证应用程序(请参阅下面的代码)。通过会话中间件快速,在请求客户端尚未拥有会话的每个请求上创建会话。我想在登录后分配会话,或者在登录后创建新会话。

这是因为我最终将通过HTTPS进行登录,并希望防止黑客劫持已经过身份验证的用户的会话。

这是我的服务器代码:

// Server.js configures the application and sets up the webserver

//importing our modules
var express = require('express');
var app = express();
var port = process.env.PORT || 8080;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var MongoStore = require('connect-mongo')(express);

var configDB = require('./config/database.js');

//Configuration of Databse and App

mongoose.connect(configDB.url); //connect to our database

require('./config/passport')(passport); //pass passport for configuration

app.configure(function() {

    //set up our express application

    app.use(express.logger('dev')); //log every request to the console
    app.use(express.cookieParser()); //read cookies (needed for auth)
    app.use(express.bodyParser()); //get info from html forms
    app.set('view engine', 'ejs'); //set up ejs for templating

    //configuration for passport
    app.use(express.session({ secret: 'olhosvermdfgytuelhoseasenhaclassica',
         cookie: {
            maxAge: 120000 },
         store:
             new MongoStore({
                db: 'xYrotr4h',
                host: 'novus.modulusmongo.net',
                port: 27017,
                username: 'gdog',
                password: 'fakepassowrd123'
            })
         })); //session secret + expiration + store
    app.use(passport.initialize());
    app.use(passport.session()); //persistent login session
    app.use(flash()); //use connect-flash for flash messages stored in session

});

//Set up routes
require('./app/routes.js')(app, passport);

//launch
app.listen(port);
console.log("Server listening on port" + port);

在我的新Passport本地策略中,当用户成功验证数据库但导致服务器崩溃时,我尝试使用req.session.regenerate()或req.session.reload()。

以下是我定义策略的方法:

//Passport.js sets up our local strategies

//imports

var LocalStrategy = require('passport-local').Strategy;
var User = require('../app/models/user');

//export this as a module since we give it to passport

module.exports = function(passport) {
    //Set up the session for persistent login

    passport.serializeUser(function(user, done) {
        done(null, user.id);
    });

    //used to serialize the user
    passport.deserializeUser(function(id, done) {
        User.findById(id, function(err, user) {
            done(err, user);
        });
    });

    //setting up local sign up

    passport.use('local-signup', new LocalStrategy({
            //by default, the local strategy uses usernames and password, we will override with email
            usernameField: 'email',
            passwordField: 'password',
            passReqToCallback: true
        },
        function(req, email, password, done) {
            console.log("Callback ran!");
            //asynchronous
            //User.findOne wont fire unless data is sent back
            process.nextTick(function() {
                console.log("I did run!");
                //find user whose email is the same as form email
                // we are checking to see if the user trying to sign up already exists
                User.findOne({ 'local.email': email }, function(err, user) {
                    //if there any errors, return the errors
                    if (err) {
                        return done(err);
                    }
                    //check to see if there any users already with that email
                    if (user) {
                        return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
                    } else {
                        console.log('New user will be added to the DB!');
                        //if there is no user with that e-mail, create the user
                        var newUser = new User();

                        //we set the user's local credentials
                        newUser.local.email = email;
                        newUser.local.password = newUser.generateHash(password);

                        //save the user in the store
                        newUser.save(function(err) {
                            if (err) {
                                throw err;
                            }
                            return done(null, newUser);
                        });
                    }
                });
            });
        }));

        // =========================================================================
    // LOCAL LOGIN =============================================================
    // =========================================================================
    // we are using named strategies since we have one for login and one for signup
    // by default, if there was no name, it would just be called 'local'

    passport.use('local-login', new LocalStrategy({
        // by default, local strategy uses username and password, we will override with email
        usernameField : 'email',
        passwordField : 'password',
        passReqToCallback : true // allows us to pass back the entire request to the callback
    },
    function(req, email, password, done) { // callback with email and password from our form

        // find a user whose email is the same as the forms email
        // we are checking to see if the user trying to login already exists
        User.findOne({ 'local.email' :  email }, function(err, user) {
            // if there are any errors, return the error before anything else
            if (err)
                return done(err);

            // if no user is found, return the message
            if (!user)
                return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash

            // if the user is found but the password is wrong
            if (!user.validPassword(password))
                return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata

            // all is well, return successful user
            // removing the req.session.regenerate fixes any crashing
            req.session.regenerate(function(err, done, user){
                    return done(null, user);
                 });

        });

    }));

};

4 个答案:

答案 0 :(得分:16)

在深入了解护照和快递会图书馆后,我发现了它!

var session = function (req, res) {
    var temp = req.session.passport; // {user: 1}
    req.session.regenerate(function(err){
        //req.session.passport is now undefined
        req.session.passport = temp;
        req.session.save(function(err){
            res.send(200);
        });
    });
};

app.post('/login', passport.authenticate('local'), session);

基本上我让护照先进行身份验证,然后将对象附加到req.session.passport。 Passport使用此对象查找会话映射 - > userId进一步请求。重新生成会话时,req.session.passport对象将丢失。因此,您必须确保将其转移到新生成的会话,并保存它。

答案 1 :(得分:0)

看起来Jared并不想直接根据issue #194来支持这一点,我不确定我是否同意 - 至少,护照应该暴露自己的会话重新生成功能。无论如何,你可以通过替换:

来解决这个问题
req.session.regenerate(function(err, done, user){
    return done(null, user);
});

有这样的事情:

var passport = req._passport.instance;
req.session.regenerate(function(err, done, user) {
    req.session[passport._key] = {};
    req._passport.instance = passport;
    req._passport.session = req.session[passport._key];
    return done(null, user);
});

答案 2 :(得分:0)

Gitter.im开源项目中的真实示例:https://gitlab.com/gitlab-org/gitter/webapp/commit/44bb6d8934bce37b86d4ee3fcdba759967a5e5c1

扩展Steven Yang's答案:

在使用custom callbacks的情况下,您有多种策略,您想创建一个单独的方法(例如passportLogin),该方法最终将调用req.login

//passportLogin.js

async function regeneratePassportSession(req) {
  const passportSession = req.session.passport;
  return new Promise((resolve, reject) =>
    req.session.regenerate(function(err) {
      if (err) reject(err);
      assert(!req.session.passport);
      req.session.passport = passportSession;
      req.session.save(function(err) {
        if (err) reject(err);
        resolve();
      });
    })
  );
}

/**
 * Adds user to passport, if this is the
 * first time (user just logged in) we generate a new session
 * and returns a user with identity object
 */
module.exports = async function passportLogin(req, user) {
  // if user just logged in (session hasn't been authenticated before)
  if (!req.user) await regeneratePassportSession(req);
  await new Promise((resolve, reject) => {
    req.login(user, err => {
      if (err) reject(err);
      resolve();
    });
  });
  return user;

在每一个策略中都称呼它。

答案 3 :(得分:-1)

我认为你需要替换:

req.session.regenerate(function(err, done, user){
   return done(null, user);
});

使用:

  req.login(user, function(err) {
    if (err) return res.status(500).send('error');
    return done(null,user); 
  });

req.login在内部调用您的passport.serializeUser()功能。