Node.js
Express
npm
Passport

Express4 + Passportでログイン認証をしてみる

More than 1 year has passed since last update.

はじめに

Passportとはログイン認証に便利なライブラリ。
ただ巷で溢れるサンプルはだいたいExpress3.xで書かれているので、Express4用にメモ。
OAuthだとかOpenIDを使った認証もあるけど、
今回はシンプルにユーザー、パスワードのみで認証してみます。

詳しくはこちら。コードがExpress3だけど。
Passport(公式ページ)
日本語訳ページ

バージョン

node.js - 0.12.7
express - 4.13.1
passport - 0.3.2

実装

まずはモデル。認証情報を保持する。

account.js
'use strict';

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var AccountSchema = new Schema({
    id: { type: String, required: true },
    password: { type: String, required: true },
    updated_at: { type: Date },
    created_at: { type: Date }
});

AccountSchema.pre('save', function(next) {
    var now = new Date();
    this.updated_at = now;
    if (!this.created_at) {
        this.created_at = now;
    }
    next();
});

module.exports = mongoose.model('Account', AccountSchema);

次にapp.js。

app.js
var express = require('express');
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var session = require('express-session');
var crypto = require('crypto');
var app = express();

app.use(session({ secret: 'hoge' }));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
var LocalStrategy = require('passport-local').Strategy;

// 認証
passport.use(new LocalStrategy(
    {
        usernameField: 'name',
        passwordField: 'password',
        passReqToCallback: true
    },
    function (req, name, password, done) {
        process.nextTick(function () {
            var Account = mongoose.model('Account');
            Account.findOne({ "id": name }, function (err, account) {
                if (err) return done(err);
                if (!account) {
                    req.flash('error', 'ユーザーが見つかりませんでした。');
                    req.flash('input_id', name);
                    req.flash('input_password', password);
                    return done(null, false);
                }
                var hashedPassword = getHash(password);
                if (account.password != hashedPassword
                    && account.password != password) {
                    req.flash('error', 'パスワードが間違っています。');
                    req.flash('input_id', name);
                    req.flash('input_password', password);
                    return done(null, false);
                }
                return done(null, account);
            });
        })
    }
));

// 暗号化
var getHash = function(value) {
    var sha = crypto.createHmac('sha256', 'secretKey');
    sha.update(value);
    return sha.digest('hex');
};

// passport
passport.serializeUser(function (account, done) {
    done(null, account.id);
});
passport.deserializeUser(function (serializedAccount, done) {
    var Account = mongoose.model('Account');
    Account.findOne({ "id": serializedAccount }, function (err, account) {
        done(err, account.id);
    });
});

routingとかsessionの有無チェックとかは省略しております。
ポイントはpassReqToCallbacktrueにしてリクエストを取得すること。
で、connect-flashのライブラリを読み込んで、入力値やエラーメッセージを設定しています。
usernameFieldpasswordField はhtmlのテキストフィールド'name'値です。

次にログイン処理。

login.js
'use strict';

var express = require('express');
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var router = express.Router();

// ログイン画面表示
router.get('/', function (req, res) {
    res.render('login', {
        error: req.flash('error'),
        input_id: req.flash('input_id'),
        input_password: req.flash('input_password')
    });
});

// ログイン情報入力
router.post('/', function(req, res, next) {
    passport.authenticate('local', {
        successRedirect: '/',
        failureRedirect: '/login',
        failureFlash: true
    })(req, res, next);
});

module.exports = router;

app.jsflashした値をここで取得してrenderしてやる。

login.ejs
<!DOCTYPE html>
<html lang="ja">
<body>
    <p><%= error %</p>
    <form action="/login" method="POST">
    ID:
    <input type="text" name="name" value="<%= input_id %>">
    Password:
    <input type="password" name="password" value="<%= input_password >">
    <input type="submit" value="ログイン">
    </form>
</body>
</html>