一覧へ

Passport.js — 認証ミドルウェア

Passport.jsの構造とローカルストラテジーを理解し、Expressでログイン認証を実装します。

中級
|
12
|
検証済み (2026-07)
Passport.js認証ミドルウェアローカルストラテジーシリアライズセッション認証
進捗0/55 (0%)

Passport.js — 認証ミドルウェア

このトピックを終えると

Passport.js がなぜ必要なのかを理解し、Local Strategy を使用して ID/パスワード認証を実装できるようになり、serialize/deserialize の役割を説明できるようになります。


なぜ Passport.js を使うのか

前のトピックで、セッションベースの認証を直接実装しました。それは機能しますが、認証方式が多様になるとコードが複雑になります。

  • ID/パスワードログイン
  • Google ログイン
  • GitHub ログイン
  • JWT トークン認証

それぞれを直接実装すると、ルートごとに認証ロジックが分散してしまいます。Passport.js は、この問題を解決する認証専門ミドルウェアです。500 種類以上の認証ストラテジー (Strategy) をプラグイン形式で提供します。


主要な概念 — Strategy パターン

Passport.js は Strategy パターンを使用します。「どのように認証するか」を Strategy オブジェクトに分離します。

text
Passport.js (フレームワーク)
├── Local Strategy    → ID/パスワード
├── Google Strategy   → Google OAuth
├── GitHub Strategy   → GitHub OAuth
└── JWT Strategy      → JSON Web Token

各 Strategy は独立した npm パッケージです。必要なものだけインストールして使用します。


インストール

bash
npm install passport passport-local express-session
  • passport: コアライブラリ
  • passport-local: ID/パスワード認証ストラテジー
  • express-session: セッション管理 (Passport が内部で使用)

Local Strategy の設定

javascript
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const app = express();

// 仮想のユーザー DB
const users = [
  { id: 1, username: 'alice', password: 'pass123', displayName: 'Alice Kim' },
  { id: 2, username: 'bob', password: 'pass456', displayName: 'Bob Lee' }
];

// 1. Strategy を登録
passport.use(new LocalStrategy(
  (username, password, done) => {
    const user = users.find(u => u.username === username);
    if (!user) {
      return done(null, false, { message: 'User not found' });
    }
    if (user.password !== password) {
      return done(null, false, { message: 'Wrong password' });
    }
    return done(null, user);
  }
));

done コールバックの 3 つの呼び出しパターン:

呼び出し意味
done(null, user)認証成功。user オブジェクトを渡す
done(null, false, {message})認証失敗。ユーザーが存在しない、またはパスワードが一致しない
done(err)システムエラー。DB への接続失敗など

Serialize / Deserialize

セッションにユーザー全体のオブジェクトを保存すると、メモリを無駄にします。Passport は、セッションに 最小限の識別情報 (通常は ID) のみを保存し、リクエストごとにその ID を使用してユーザーを復元します。

javascript
// 2. セッションに保存するデータ (ログイン時に 1 回)
passport.serializeUser((user, done) => {
  done(null, user.id);  // セッションに user.id のみ保存
});

// 3. セッションからユーザーを復元 (各リクエストごと)
passport.deserializeUser((id, done) => {
  const user = users.find(u => u.id === id);
  done(null, user);     // req.user にユーザーオブジェクト全体を設定
});
text
ログイン時:   user オブジェクト → serializeUser → セッションに id=1 を保存
各リクエスト時:  セッションから id=1 → deserializeUser → user オブジェクトを復元 → req.user

deserializeUser で DB をクエリすると、セッションには ID のみ保存しながらも、各リクエストで最新のユーザー情報を利用できます。


ミドルウェアの接続

javascript
// 4. ミドルウェアの設定 (順序が重要!)
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(session({
  secret: process.env.SESSION_SECRET || 'dev-secret',
  resave: false,
  saveUninitialized: false
}));
app.use(passport.initialize());   // Passport を初期化
app.use(passport.session());      // セッションと接続

順序が重要です: express-sessionpassport.initialize()passport.session()。順序が異なると、セッションが正しく機能しません。


ログイン / ログアウト ルート

javascript
// ログイン
app.post('/login',
  passport.authenticate('local', {
    successRedirect: '/dashboard',
    failureRedirect: '/login',
    failureMessage: true
  })
);

// または JSON 応答を希望する場合
app.post('/api/login', (req, res, next) => {
  passport.authenticate('local', (err, user, info) => {
    if (err) return next(err);
    if (!user) {
      return res.status(401).json({ error: info.message });
    }
    req.logIn(user, (err) => {
      if (err) return next(err);
      res.json({ message: `Welcome, ${user.displayName}` });
    });
  })(req, res, next);
});

// ログアウト
app.post('/logout', (req, res) => {
  req.logout((err) => {
    if (err) return res.status(500).json({ error: 'Logout failed' });
    res.json({ message: 'Logged out' });
  });
});

passport.authenticate('local') は、登録済みの LocalStrategy を実行します。成功すると serializeUser が呼び出され、セッションにユーザー ID が保存されます。


保護されたルート

javascript
function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    return next();
  }
  res.status(401).json({ error: 'Login required' });
}

app.get('/dashboard', ensureAuthenticated, (req, res) => {
  res.json({
    message: `Hello, ${req.user.displayName}`,
    user: { id: req.user.id, username: req.user.username }
  });
});

req.isAuthenticated() は Passport が提供するメソッドで、セッションに有効なユーザーが存在する場合は true を返します。req.user は、deserializeUser で復元されたユーザーオブジェクトです。


全体の流れを整理

text
1. サーバーの起動
   passport.use(LocalStrategy)    — 認証方法を登録
   passport.serializeUser()       — セッションに保存する情報を登録
   passport.deserializeUser()     — セッションから情報を復元する方法を登録

2. POST /login
   passport.authenticate('local') — Strategy を実行
   → 成功: serializeUser → セッションに ID を保存 → 応答
   → 失敗: 401 応答

3. GET /dashboard (認証が必要)
   セッションクッキーを確認 → deserializeUser → req.user を設定
   ensureAuthenticated → req.isAuthenticated() → true → ルートを実行

4. POST /logout
   req.logout() → セッションからユーザーを削除 → 応答

よくある間違い

間違い結果解決
app.use(session(...)) の前に passport.session() を呼び出すセッションが初期化されないexpress-session → passport.initialize() → passport.session() の順序
serializeUser でユーザーオブジェクト全体を保存するセッションサイズが膨大になるuser.id のみを保存
deserializeUser で非同期エラーを無視する認証失敗時にサーバーがクラッシュするdone(err) を呼び出す
Strategy コールバックで done() の呼び出しを忘れるリクエストが無限に待機するすべての分岐で done() が呼び出されていることを確認する

主要なまとめ

概念まとめ
Passport.js認証専門ミドルウェア。Strategy パターンでさまざまな認証方式をサポート
Strategy認証方法をカプセル化されたオブジェクト (Local, Google, JWT など)
serializeUserセッションに保存する最小限の情報を決定する (通常は user.id)
deserializeUser各リクエストごとにセッションの ID からユーザーオブジェクトを復元する
req.userdeserializeUser で復元された、現在のログインユーザー
req.isAuthenticated()ログイン状態を確認する

Passport.js の価値は、「認証ロジックの標準化」にあります。Local から Google OAuth に変更する場合、Strategy のみを置き換えるだけで済みます。serialize/deserialize、ミドルウェアチェーン、req.user — それ以外のコードはそのまま維持できます。

💬 質問・コメント

0件のコメント

ログインせずに投稿できます。ゲスト投稿は投稿者自身で編集・削除できません。

0/2000

読み込み中...