nodeサーバーで express session と passport.js を用いた認証処理

# express session と passport.js を用いた認証処理

SPA アプリケーションのサーバーサイドでexpressフレームワークで作成したサーバーの認証処理について、
苦労した点が多かったのでまとめます。仕様は以下の通りです。
  
- フォーム認証
- passport.js を使用する
- セッション管理は MemoryStore

## １．認証処理の実装

認証処理を行うためのクラス Authenticetor.js を作成します。
memorystore モジュールを用いている理由としては、
express session で MemoryStore への保存する場合、 メモリリークを起こす可能性があるためは推奨されていません。
そのため、memorystore モジュールを用いています。
本来は redisDB などを用いた管理にするほうが懸命だと思います。  

```javascript
import passport from 'passport';
import LocalStrategy from 'passport-local';
import session from 'express-session';
import uuid from 'uuid';
const MemoryStore = require('memorystore')(session);
// ユーザー取得クラス
import User from './User';

/**
 * 認証処理を行うためのクラス
 */
export default class Authenticator {

/**
   * 初期処理
   * @param {Object} app
   */
  static initialize(app) {
    this._createSession(app);
    app.use(passport.initialize());
    app.use(passport.session());
    this.serialize();
    this.deserialize();
  }

  /**
   * セッション作成
   * @param {Object} app
   */
  static _createSession(app) {
    app.use(
      session({
        cookie: { maxAge: 6000000},
        store: new MemoryStore({
          checkPeriod: 6000000
        }),
        genid: () => {
          return uuid();
        },
        secret: 'しーくれっとこーど',
        rolling: true,
        resave: false,
        saveUninitialized: true
      })
    );
  }

  /**
   * セッション格納処理
   */
  static setStrage() {
    passport.use(
      new LocalStrategy(
        { usernameField: 'id' },
        async (id, password, done) => {
          try {
            // ユーザー情報取得
            const user = User.get(id);
            if (user && user.id === id && user.pass === pass) {
              // ユーザー情報を返却
              return done(null, user);
            }
            done(null, false);
          } catch (error) {
            done(null, false);
          }
        }
      )
    );
  }

  /**
   * serialize処理
   */
  static serialize() {
    passport.serializeUser((user, done) => {
      done(null, user);
    });
  }

  /**
   * deserialize処理
   */
  static deserialize() {
    passport.deserializeUser(async (req, id, done) => {
      done(null, {id});
    });
  }
}
```

## ２．認証APIにリクエストした際の認証処理
ログインするためのAPIの処理を記述します。

```javascript
const express = require('express');
const router = express.Router();
const passport = require('passport');

router.post('/', passport.authenticate('local'), async (req, res) => {
  try {
    const userData = {
        id: req.id,
        hoge: 'fuga'
    }
    // 何かクライアント側に送りつけたい場合
    req.session.user = userData;
    res.send(req.user);
  } catch (error) {
    res.sendStatus(500);
  }
});
```

## ３．app.jsの設定
上記１.で作成したAuthenticatorクラスをapp.jsから呼び出します。
そして、認証のリクエスト以外すべてのリクエストに対して認証チェックを掛け、
上記２．で作成したエンドポイントに認証のルーティングをします。

```javascript
import Authenticator from './modules/Authenticator';
:
:
const app = express();
Authenticator.initialize(app);
Authenticator.setStrage();
:
// 認証ルート以外のすべてのルートをチェック
app.use(/^(?!singin)/, function(req, res, next) {
  if (!req.isAuthenticated()) {
    res.sendStatus(401);
    return;
  } 
  next();
});
app.use('/singin', singin);
:

```

# まとめ
実装中はめっちゃ苦労しましたが、まとめてみると短いコードでセッション認証をかけることができていると思います。
JWTトークンを用いた場合も似たような形で実装できました。認証もうこわくない！