不会调用DeserializeUser,也不会通过Express和PassportJS将cookie发送给客户端

时间:2019-08-21 19:29:00

标签: node.js express passport.js

我有一个带有passwordJS本地策略身份验证的快速应用程序。该应用程序已部署到Heroku的免费层(*.herokuapp.com)。在生产中,将cookie.secure设置为true时,通过https不会调用deserializeUser,也不会将cookie发送给客户端。
我知道这个问题已经问了很多,而且我已经寻找了一段时间。让我发疯的是,在实施了两天的不同解决方案之后,例如在this post中,我终于在昨天开始工作了。但是现在它不再工作了。我可以在日志中看到用户和会话对象,但是未调用deserializeUserreq.isAuthenticated()passport.authenticate()之外的任何地方都返回false,并且客户端中没有cookie。
可能有用的其他信息是,我正在使用Knex会话存储来存储会话,而我正在将PostgreSQL与Knex一起使用。
以下是相关代码:

servsr.js

const express = require('express');
const helmet = require('helmet');
const bodyParser = require('body-parser');
const cors = require('cors');
const passport = require('passport');
require('dotenv').config();

const dev_env = process.env.LOCAL_DEV;

const knex = require('knex')({
  client: `pg`,
  connection: {
    host: dev_env ? process.env.DEV_DB_HOST : null,
    user: dev_env ? process.env.DEV_USER : null,
    password: dev_env ? process.env.DEV_PASSWORD : null,
    database: dev_env ? process.env.DEV_DB_NAME : null,
    connectionString: dev_env ? null : process.env.DATABASE_URL,
    ssl: dev_env ? null : true
  }
});

const app = express();

app.set('trust proxy', true);

app.use(cors({ origin: true, credentials: true }));
app.use(function (req, res, next) {
  res.header('Access-Control-Allow-Credentials', true);
  res.header('Access-Control-Allow-Origin', req.headers.origin);
  res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
  res.header('Access-Control-Allow-Headers', 'Origin, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Response-Time, X-PINGOTHER, X-CSRF-Token, Authorization');
  if (req.method === "OPTIONS") {
      return res.status(200).end();
  } else {
      next();
  }
});

app.use(helmet());
app.use(bodyParser.json({ limit: '5mb' }));

require('./passport')(app);

const signin = require('./controllers/signin');
const uploadData = require('./controllers/uploadData');
const getData = require('./controllers/getData');

app.get('/', (req, res) => { res.send('its working!'); })

app.post('/signin', (req, res, next) => { signin.handleSignin(req, res, next, passport) })

// app.get('/success', (req, res) => res.status(200).json(true))
// app.get('/fail', (req, res) => res.status(400).json('fail'))

app.get('/log-out', (req, res) => {
  req.logOut();
  res.clearCookie(process.env.COOKIE_SECRET);
  res.status(200).json('logged out ');
})

app.post('/upload-data', (req, res) => { 
  console.log('upload-data req.headers.cookie: ', req.headers.cookie)
  console.log('upload-data req.protocol: ', req.protocol)
  if (req.isAuthenticated()) {
    uploadData.handleUploadData(req, res, knex) 
  } else {
    res.status(400).json('request is not authenticated');
  }
})

app.get('/get-data', (req, res) => {
  console.log('get-data req.headers.cookie: ', req.headers.cookie)
  console.log('get-data req.protocol: ', req.protocol)
  if (req.isAuthenticated()) {
    getData.handleDataRequest(req, res, knex)
  } else {
    res.status(400).json('request is not authenticated');
  }
})

app.listen(process.env.PORT, () => {
  console.log(`listening on port ${process.env.PORT}`);
})

passport.js

const session = require('express-session');
const passport = require('passport');
const bcrypt = require('bcrypt');
const KnexSessionStore = require('connect-session-knex')(session);

const dev_env = process.env.LOCAL_DEV;

const knex = require('knex')({
  client: `pg`,
  connection: {
    host: dev_env ? process.env.DEV_DB_HOST : null,
    user: dev_env ? process.env.DEV_USER : null,
    password: dev_env ? process.env.DEV_PASSWORD : null,
    database: dev_env ? process.env.DEV_DB_NAME : null,
    connectionString: dev_env ? null : process.env.DATABASE_URL,
    ssl: dev_env ? null : true
  }
});

const store = new KnexSessionStore({ knex });

module.exports = (app) => {
  app.use(session({
    secret: process.env.COOKIE_SECRET,
    saveUninitialized: false,
    resave: false,
    rolling: true,
    store,
    cookie: {
      httpOnly: true,
      maxAge: 1000 * 60 * 60 * 2, // 2 hours
      secure: true,
      // domain: process.env.DOMAIN,
      path: '/'
    } 
  }));

  app.use(passport.initialize());
  app.use(passport.session());

  const LocalStrategy = require('passport-local').Strategy;

  passport.use(new LocalStrategy((username, password, done) => {
    knex.select('id', 'name', 'hash').from('login')
    .where('name', '=', username)
    .then(user => {
      if (!user) {
        return done(null, false);
      }

      const isValid = bcrypt.compareSync(password, user[0].hash);
      if (!isValid) {
        return done(null, false);
      }

      return done(null, user[0]);
    })
    .catch(err => {
      console.log(err);
      return done(err);
    })
  }));

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

  passport.deserializeUser((id, done) => {
    console.log('deserialize!')
    knex.select('id').from('login')
    .where('id', '=', id)
    .then(id => {
      done(null, id)
    })
    .catch(err => {
      console.log(err)
      done(err, null)
    });
  });
}

signin.js

const handleSignin = (req, res, next, passport) => {
  console.log('signin req.headers.cookie: ', req.headers.cookie);
  console.log('signin req.protocol: ', req.protocol);
  const { username, password } = req.body;
  if (!username || !password) {
    return res.status(400).json('Incorrect form submission');
  }

  passport.authenticate('local', (err, user) => {
    if (err) {
      return next(err);
    }
    if (!user) {
      return res.status(400).json('fail');
    }
    req.logIn(user, (err) => {
      if (err) {
        return next(err);
      }
      return res.status(200).json(true);
    });
  })(req, res, next) 
}


module.exports = {
  handleSignin
};

这是我的package.json

{
  "name": "symbiosis-server",
  "version": "1.0.0",
  "description": "Server for industrial symbiosis data analysis app.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "nodemon server.js"
  },
  "author": "Ori Perelman",
  "license": "ISC",
  "devDependencies": {
    "nodemon": "^1.19.1"
  },
  "dependencies": {
    "bcrypt": "^3.0.6",
    "body-parser": "^1.19.0",
    "connect-session-knex": "^1.4.0",
    "cors": "^2.8.5",
    "dotenv": "^8.0.0",
    "eslint": "^6.1.0",
    "express": "^4.17.1",
    "express-session": "^1.16.2",
    "helmet": "^3.20.0",
    "knex": "^0.19.0",
    "passport": "^0.4.0",
    "passport-local": "^1.0.0",
    "pg": "^7.11.0"
  }
}

正如我所说,这只是在昨天才有效,现在并非没有任何改变!所以我真的迷路了,不胜感激。 谢谢!

更新(25/8/19):

因此,在尝试查找问题之后,我在cookie.domain设置中将其范围缩小到express-session。设置为cookie.domain时,该cookie由会话设置,我可以在数据库中看到它,但是由于某种原因,客户端未接收到它。我可以看到的另一件事是,设置了cookie.domain之后,deserializeUser()永远不会被调用。
至于身份验证先工作然后中断的问题,我能提出的唯一解释是,我显然忘记了删除某个地方的旧cookie,并且一直保持有效,直到过期...
我对代码做了一些修改,并在上面进行了更新。我进行的一项更改是添加app.set('trust proxy', true),因为我注意到req.protocol返回http而不是https。它修复了协议,但没有解决问题。
因此,我想我将通过快速会话解决这个问题。但是,如果有人对cookie.domain为何会破坏身份验证,甚至对解决方法有任何想法,我都会很乐意听到。

谢谢,我希望这可以对某人有所帮助。

0 个答案:

没有答案