Expressでログイン機能を実装してみる

--sessionオプションをつけると、ssessionオブジェクトが有効な状態でプロジェクトが作成されます。

 

$ express --sessions

 

既存のプロジェクトでセッション管理が必要になった場合は、app.jsのapp.use(app.router);の直ぐ上くらいに、以下の二行を追加します。

 

app.use(express.cookieParser('your secret here'));
app.use(express.session());

 

これで、sessionオブジェクトを使ってセッション管理ができるようになります。

 

以下が、そのsessionオブジェクトを使ったログイン機能の実装例です。

ほぼ、Expressのexampleのコピーですが、MongoDBなど使おうとするとこんな感じになるかなぁと思います。

 

// routes/index.js
var schema = require('../models')
  , User = schema.User
  , helpers = require('../lib/helpers');
 
exports.register = function(req, res) {
  var email = req.body.email
    , passwd = req.body.passwd;
 
  var user = new User({ email: email });
  user.hashPasswd(passwd, function(err) {
    if (err) return res.redirect('register');
 
    user.save(function(err) {
      if (err) return res.redirect('register');
      res.redirect('/');
    });
  })
}
 
exports.login = function(req, res) {
  helpers.authenticate(req.body.email, req.body.passwd, function(err, user) {
    if (user) {
      req.session.regenerate(function() {
        req.session.user = user;
        res.redirect('/');
      });
    } else {
      req.session.error = 'Authentication failed.';
      res.redirect('login');
    }
  });
}
 
exports.logout = function(req, res) {
  req.session.destroy(function() {
    res.redirect('/');
  })
} 

 

// models/user.js
var Schema = require('mongoose').Schema
  , helpers = require('../lib/helpers');
 
var Schema = module.exports = new Schema({
  email: { type: String, unique: true },
  salt: String,
  hash: Buffer,
});
 
Schema.statics.findByEmail = function(email, fn) {
  this.findOne({ email: email }, function(err, user){
    if (err || !user) return fn('cannot find user');
    fn(null, user);
  });
}
 
Schema.methods.hashPasswd = function(passwd, fn) {
  var self = this;
 
  if (!self.salt || !self.hash) {
    helpers.hash(passwd, function(err, salt, hash){
      self.salt = salt;
      self.hash = hash;
      fn(err);
    });
  } else {
    fn(null);
  }
}

 

 // lib/helpers.js
var crypto = require('crypto')
  , schema = require('../models')
  , User = schema.User;
 
var hash = exports.hash = function(passwd, salt, fn) {
  var len = 128
    , iterations = 12000;
 
  if (3 == arguments.length) {
    crypto.pbkdf2(passwd, salt, iterations, len, fn);
  } else {
    fn = salt;
    crypto.randomBytes(len, function(err, salt){
      if (err) return fn(err);
      salt = salt.toString('base64');
      crypto.pbkdf2(passwd, salt, iterations, len, function(err, hash){
        if (err) return fn(err);
        fn(null, salt, hash);
      });
    });
  }
}
 
exports.authenticate = function (email, passwd, fn) {
  User.findByEmail(email, function(err, user) {
    if (err) return fn(err);
    
    hash(passwd, user.salt, function(err, hash) {
      if (err) return fn(err);
      if (hash == user.hash) return fn(null, user);
      fn(new Error('invalid password'));
    });
  }); 
}