web-dev-qa-db-ja.com

Express-mysql-sessionがパスポートdeserializeUserの実行を妨げている

私はpassport.jsを使用してFacebook経由でユーザーにログインするアプリを持っています。そして、express-mysql-sessionを使用してログイン状態を維持しようとしています。 express-mysql-sessionコードを含めない場合、パスポートのserializeUser関数とdeserializeUser関数は正常に動作します...ただし、express-mysql-sessionでセッションを保存しようとするコードのコメントを外すと、deserializeUser関数は失敗しますヒットしないし、ユーザーが適切にログインすることはありません。

server.jsファイル

var express      = require('express');
var mysql        = require('mysql');
var passport     = require('passport');
var session      = require('express-session');
var MySQLStore   = require('express-mysql-session')(session);

if (typeof process.env.OPENSHIFT_MYSQL_DB_Host === "undefined"){
    var options = {
        Host     : 'localhost',
        port     : '3307',
        user     : 'user',
        password : 'password',
        database : 'database',
        socketpath: '/var/run/mysqld/mysqld.sock'
    }
} else { 
    var options = {
        Host     : process.env.OPENSHIFT_MYSQL_DB_Host,
        port     : process.env.OPENSHIFT_MYSQL_DB_PORT,
        user     : process.env.OPENSHIFT_MYSQL_DB_USERNAME,
        password : process.env.OPENSHIFT_MYSQL_DB_PASSWORD,
        database : process.env.OPENSHIFT_APP_NAME,
        socket   : process.env.OPENSHIFT_MYSQL_DB_SOCKET
    }
};    

var connection = mysql.createConnection(options);

var sessionStore = new MySQLStore({
    checkExpirationInterval: 900000,// How frequently expired sessions will be cleared; milliseconds.
    expiration: 86400000,// The maximum age of a valid session; milliseconds.
    createDatabaseTable: false,// Whether or not to create the sessions database table, if one does not already exist.
    connectionLimit: 1,
    schema: {
        tableName: 'LoginRequests',
        columnNames: {
            session_id: 'loginID',
            expires: 'expires',
            data:'data'
        }
    }
}, connection);

 self.initializeServer = function() {
        self.app = module.exports = express();
        self.app.configure(function() {
            self.app.set('views', __dirname + '/public');
            self.app.set('view engine', 'html');
            self.app.engine('html', require('hogan-express'));
            self.app.enable('view cache');
            self.app.use(express.favicon());
            self.app.use(express.logger('dev'));
            self.app.use(express.bodyParser());
            self.app.use(express.methodOverride());
            self.app.use(express.cookieParser('secret'));
            self.app.use(session({
                key: 'session_cookie_name',
                secret: 'secret',
                cookie: {maxAge: 3600000, secure:false},
                store: sessionStore,
                resave: false,
                saveUninitialized: false
            }));
            // required for passport
            self.app.use(passport.initialize());
            self.app.use(passport.session()); // persistent login sessions
            self.app.use(express.static(path.join(__dirname, 'public')));
            self.app.use('/public',express.static(__dirname, '/public'));
            self.app.use(self.app.router);
            //self.app.use(require('stylus').middleware(__dirname + '/public'));


        });

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

    }

したがって、上記のセッションオブジェクトの「保存」オプションをコメントアウトすると、パスポート関数がヒットします。この行をコメント化しないでおくと、deserializeUser関数がヒットしません。

パスポート機能

passport.serializeUser(function(user, done) {
        console.log('you have been serialized!');
            done(null, user.id);
    });


    // used to deserialize the user
    passport.deserializeUser(function(id, done) {
        console.log('you have been deserialized!');
        connection.query("SELECT * FROM Users WHERE id = "+id,function(err,rows){
            done(err, rows[0]);
        });
    });

編集

Mor Pazは、デバッグモジュールでサーバーを実行したときのログの一部を含めることを提案しました。以下は、ユーザーがシリアル化される直前と直後のログです。ユーザーは、この近くのある時点で逆シリアル化する必要がありますが、そうではありません。

GET /auth/facebook 302 81ms - 412b
express-mysql-session:log Getting session: oNcJ4UapxCY_zKOyfSBTUWaVhaNZuFRq +356ms
you are a user!
you have been serialized!
  express-mysql-session:log Setting session: tgRPY-Mb1VDP2zaSMOFhlf_IWFhVpTia +798ms
  express-mysql-session:log Getting session: tgRPY-Mb1VDP2zaSMOFhlf_IWFhVpTia +6ms
GET /auth/facebook/callback?    code=AQCWPvA5ZRMYoTueW6_wWU49Up5ggjW68ufOtiYkU5IzhRjSNyyWnzlQVprgQo_uubQkEVvNI0yo53ET3cWBnDAHUGmAXPBy_ITEmC-biE2KEGEr0iCm_cqjuG90nnePY-k9U2oFUbX2kvLgMeM0kZ-094EHiU_NJjmAJNj6mzTkSE47935RhJy0Tba_sYS88_C0N3kn5f5kcoTC4KsgW1gBHWWJAwZ68Lj94ffVe2hN97580CtzEpJa0wwQHwTBYfmjQ0NfUdx07m4rXW9R7PR06aHDcUDrYqR9Kb0LWq4sZLbQjV5rI7gzkWG-huhq7IY 302 825ms - 72b
  express-mysql-session:log Setting session: Xo9OjfmJzTFp1CSF6srLi_UyxTCLg-EI +56ms
  express-mysql-session:log Getting session: Xo9OjfmJzTFp1CSF6srLi_UyxTCLg-EI +23ms
  express-mysql-session:log Getting session: Xo9OjfmJzTFp1CSF6srLi_UyxTCLg-EI +2ms
GET /profile 200 84ms - 4.22kb
65
user2796352

問題を再現することは不可能だったので、実際の例を用意しました。 [Github repo。]

Openshift用に作成されました。これはenvironment変数の使用法を確認したためです(他の使用例にも簡単に適応できます) )。

私は元のコンセプトにいくつかの変更を加えました:

  1. 古い、廃止された(エクスプレス)バンドルミドルウェアの使用法を置き換えました。
  2. self = thisコンセプトの代わりにClassを使用する
  3. ユーザーのログインにFacebookの代わりにGithubを使用しています...
  4. 新しいユーザーをデータベースに含めるためのいくつかの基本的な機能が含まれています
  5. 一部の元のモジュールがありません(簡単に含めることができます)

出発点として役立つことを願っています。

 // .: DB Configuration :.
const mysql = require('mysql'); 
var dbconf = {Host:process.env.OPENSHIFT_MYSQL_DB_Host,port:process.env.OPENSHIFT_MYSQL_DB_PORT,user:process.env.OPENSHIFT_MYSQL_DB_USERNAME,password:process.env.OPENSHIFT_MYSQL_DB_PASSWORD,database:process.env.OPENSHIFT_APP_NAME,socket:process.env.OPENSHIFT_MYSQL_DB_SOCKET}}    
const dbconn = mysql.createConnection(dbconf); /*or create a pool*/ dbconn.connect();
// .: Express & Other Middleware Modules :.
var express = require('express');
var path = require('path');
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
var cookieParser = require('cookie-parser');
var serveStatic = require('serve-static');
// .: Sessions :.
var passport = require('passport');
var GitHubStrategy = require('passport-github2');
var session = require('express-session');
var MySQLStore = require('express-mysql-session')(session);
var sessionStoreConf = {
  connectionLimit:1,checkExpirationInterval:900000,expiration:86400000,
  createDatabaseTable:true,schema:{tableName:'LoginRequests',columnNames:{session_id:'loginID',expires:'expires',data:'data'}}
};
var sessionStore = new MySQLStore(sessionStoreConf,dbconn);
// .: Server (class) :.
class Server {
  constructor(port, ip){
    this.app = express();
    this.app.use(cookieParser('secret'));
    this.app.use(session({
      key:'session_cookie_name',
      secret:'secret',
      cookie:{maxAge:3600000,secure:false},
      store: sessionStore,
      resave:false,
      saveUninitialized:false
    }));
    this.app.use(passport.initialize());
    this.app.use(passport.session());
    this.app.use(serveStatic(path.join(__dirname,'public')))
    this.app.listen(port,ip,function(){console.log('[i] Application worker started.');});
    //require('./routes/site.js'); //~Example (routes/site.js) :
      this.app.get("/",function(req,res){res.send("<a href='./auth/github'>Click here to login (GitHub)</a>");})
      this.app.get('/auth/github',passport.authenticate('github',{scope:['user:email']}));
      this.app.get('/auth/github/callback',passport.authenticate('github',{failureRedirect:'/'}),function(req,res){res.redirect('/success');});
      // route for valid logins
      this.app.get('/success', function(req, res){ 
        if(req.user){ console.log(req.user);  res.send(req.user); }
        else{ res.redirect('/login'); }
      });
      // route to check the sessionStore table entries in the browser
      this.app.get('/sessions',function(req,res){
        dbconn.query("SELECT * FROM LoginRequests",function(err,rows){
          if(err){console.log(err);}else{
            if(rows.length!=0){
              res.send(JSON.stringify(rows));
              console.log(rows);
            }else{res.send("No LoginRequests found");}
          }
        });
      });
    //require('./config/passport.js')(passport);  //~Example (config/passport.js) :
      passport.use(new GitHubStrategy(
        {clientID:"clientID",clientSecret:"clientSecret",callbackURL:"callbackURL"},
        function(token, tokenSecret, user, cb){CheckUser('github',user,cb);}
      ));
    }
}
const server = new Server(process.env.OPENSHIFT_NODEJS_PORT,process.env.OPENSHIFT_NODEJS_IP);
// .: Passport : Serialize & Deserialize User :.
passport.serializeUser(function(user, done){
 console.log('[passport] serializeUser');
 done(null,user.id);
});
passport.deserializeUser(function(id, done) {
 console.log('[passport] deserializeUser');
  dbconn.query("SELECT * FROM Users WHERE id=?",[id],function(err,rows){
  if(err){console.log(err);}else{
    if(rows.length!=0){ done(err,rows[0]); }
    else{ done(err,null); }
  }
 });
});

//:Check if user exists:
function CheckUser(platform,user,cb){
  dbconn.query("SELECT * FROM Users WHERE id=?",[user.id],function(err,rows){
  if(err){console.log(err); cb(err,null);}else{
    if(rows.length!=0){cb(null,user);}
    else{CreateUser(platform,user,cb);}
    }
  });
}
  //:Create new user:
function CreateUser(platform,user,cb){
  switch(platform){
    case "github": 
      var newUserObj  = {id:user.id,platform:platform,email:user.emails[0].value};
      dbconn.query("INSERT INTO Users SET ?",newUserObj,function(err){
        if(err){console.log(err); cb(err,null);}else{cb(null,user);}
      });
    break;
    default: console.log("[error] (createUser) : platform not implemented :",platform); cb(err,null); break;
  }
}
1
EMX