Node.js + Express4 + Passport + pug でログイン認証画面を作る(エラー表示あり)


はじめに

Webアプリケーションを作ろうとすると何かと必要になってくるログイン認証画面。
今回はExpress4、Passport、pugを使ってエラーメッセージ(フラッシュメッセージ)が表示されるものが書けたのでメモ。
わざわざデータベースを使うのが面倒だったので一時的な配列にユーザを保持しています。

ScreenNameに「user1」、Passwordに「pass1」でログインできます。

動作確認にはNode.js v8.10.0を使用しました。
このコードはMITライセンスの基で改変・再配布できるものとします。

必要パッケージ

  • express
  • express-session
  • connect-flash
  • passport
  • body-parser
  • passport-local
  • pug

コード

server.js
const path = require('path');
const express = require('express');
const session = require('express-session');
const flash = require('connect-flash');
const passport = require('passport');
const bodyParser = require('body-parser');
const { Strategy : LocalStrategy } = require('passport-local');

const app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(session({ name: 'test-sid', secret: 'hoge', resave: false, saveUninitialized: true }));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// db
class Users {
    constructor() {
        this.store = [];
    }
    create(id, screenName, password) {
        this.store.push({ id, screenName, password });
    }
    find(key, value) {
        return this.store.find(user => user[key] == value);
    }
}
const users = new Users();
users.create('u1', 'user1', 'pass1');

// strategy
passport.use(new LocalStrategy({ usernameField: 'screenName' }, (screenName, password, done) => {
    const user = users.find('screenName', screenName);
    if (user == null) {
        return done(null, false, { message: 'invalid screenName' });
    }
    if (user.password != password) {
        return done(null, false, { message: 'invalid password' });
    }

    return done(null, user);
}));

// passport
passport.serializeUser((user, done) => {
    done(null, user.id);
});
passport.deserializeUser((id, done) => {
    done(null, users.find('id', id));
});

// routings
app.get('/', (req, res) => {
    if (!req.user) {
        res.render('login', { error: req.flash('error') });
    }
    else {
        res.render('home', { user: req.user });
    }
});
app.post('/', passport.authenticate('local', {
    successRedirect: '/', failureRedirect: '/', failureFlash: true, badRequestMessage: 'empty credentials' }));

app.listen(8080, () => {
    console.log('listening on port: 8080');
});

views/login.pug
if error != ''
    p Error: #{error}

h1 Login

form(method='post' action='/')
    div
        label(for='screen-name-input') ScreenName:
        input(type='text' name='screenName' id='screen-name-input')
    div
        label(for='password-input') Password:
        input(type='text' name='password' id='password-input')
    button Login
views/home.pug
h1 home
p hello, #{user.screenName}

表示

おわりに

新しいES、皆さんもどんどん書きましょう!