passport の使い方

Simple, unobtrusive authentication for Node.js.

v0.7.0/週MIT認証
AI生成コンテンツ

この記事はAIによって生成されました。内容の正確性は保証されません。最新の情報は公式ドキュメントをご確認ください。

Passport の使い方 — Node.js の認証ミドルウェア完全ガイド

一言でいうと

Passport は、Node.js(Express / Connect)向けの認証ミドルウェアです。「ストラテジー」と呼ばれるプラグイン方式を採用しており、ローカル認証から OAuth、OpenID まで 480 以上の認証方式を統一的な API で扱えます。

どんな時に使う?

  • ユーザー名・パスワードによるログイン機能を Express アプリに実装したいとき
  • **Google / Facebook / Twitter などのソーシャルログイン(OAuth)**を導入したいとき
  • 複数の認証方式を組み合わせて使いたいとき(例:ローカル認証 + Google OAuth を同一アプリで提供)

インストール

# npm
npm install passport

# yarn
yarn add passport

# pnpm
pnpm add passport

多くの場合、認証方式に対応するストラテジーパッケージも一緒にインストールします。

# ローカル認証の場合
npm install passport-local

# Google OAuth の場合
npm install passport-google-oauth20

# JWT 認証の場合
npm install passport-jwt

TypeScript を使う場合は型定義もインストールします。

npm install -D @types/passport @types/passport-local @types/express

基本的な使い方

最もよく使われるローカル認証(ユーザー名 + パスワード)の例を示します。

import express, { Request, Response, NextFunction } from 'express';
import session from 'express-session';
import passport from 'passport';
import { Strategy as LocalStrategy } from 'passport-local';

const app = express();

// --- ダミーのユーザーデータ ---
interface User {
  id: number;
  username: string;
  password: string;
}

const users: User[] = [
  { id: 1, username: 'admin', password: 'secret123' },
];

// --- ミドルウェア設定 ---
app.use(express.urlencoded({ extended: false }));
app.use(
  session({
    secret: 'your-session-secret',
    resave: false,
    saveUninitialized: false,
  })
);
app.use(passport.initialize());
app.use(passport.session());

// --- ストラテジー設定 ---
passport.use(
  new LocalStrategy((username, password, done) => {
    const user = users.find((u) => u.username === username);
    if (!user) {
      return done(null, false, { message: 'ユーザーが見つかりません' });
    }
    if (user.password !== password) {
      return done(null, false, { message: 'パスワードが正しくありません' });
    }
    return done(null, user);
  })
);

// --- シリアライズ / デシリアライズ ---
passport.serializeUser((user: Express.User, done) => {
  done(null, (user as User).id);
});

passport.deserializeUser((id: number, done) => {
  const user = users.find((u) => u.id === id);
  done(null, user || false);
});

// --- ルート ---
app.post(
  '/login',
  passport.authenticate('local', {
    successRedirect: '/dashboard',
    failureRedirect: '/login',
  })
);

app.get('/dashboard', (req: Request, res: Response) => {
  if (!req.isAuthenticated()) {
    return res.redirect('/login');
  }
  res.send(`ようこそ、${(req.user as User).username} さん`);
});

app.post('/logout', (req: Request, res: Response, next: NextFunction) => {
  req.logout((err) => {
    if (err) return next(err);
    res.redirect('/');
  });
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

よく使う API

1. passport.use() — ストラテジーの登録

認証ストラテジーを Passport に登録します。名前を付けて複数のストラテジーを使い分けることも可能です。

import { Strategy as GoogleStrategy } from 'passport-google-oauth20';

// デフォルト名(ストラテジーが内部で持つ名前)で登録
passport.use(
  new GoogleStrategy(
    {
      clientID: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      callbackURL: '/auth/google/callback',
    },
    (accessToken, refreshToken, profile, done) => {
      // DB からユーザーを検索 or 作成
      done(null, profile);
    }
  )
);

// カスタム名で登録(同じストラテジーを複数使う場合)
passport.use(
  'admin-local',
  new LocalStrategy((username, password, done) => {
    // 管理者専用の認証ロジック
    done(null, false);
  })
);

2. passport.authenticate() — 認証の実行

ルートミドルウェアとして使用し、指定したストラテジーで認証を実行します。

// 基本的なリダイレクト方式
app.post(
  '/login',
  passport.authenticate('local', {
    successRedirect: '/dashboard',
    failureRedirect: '/login',
    failureMessage: true, // セッションにエラーメッセージを保存
  })
);

// カスタムコールバック方式(API サーバー向け)
app.post('/api/login', (req: Request, res: Response, next: NextFunction) => {
  passport.authenticate(
    'local',
    (err: Error | null, user: User | false, info: { message: string }) => {
      if (err) return next(err);
      if (!user) {
        return res.status(401).json({ message: info.message });
      }
      req.logIn(user, (err) => {
        if (err) return next(err);
        return res.json({ message: 'ログイン成功', user });
      });
    }
  )(req, res, next);
});

3. passport.serializeUser() / passport.deserializeUser() — セッション管理

セッションにユーザー情報を保存・復元するロジックを定義します。

// セッションに保存する値を決定(通常は ID のみ)
passport.serializeUser((user: Express.User, done) => {
  done(null, (user as User).id);
});

// セッションから復元する処理
passport.deserializeUser(async (id: number, done) => {
  try {
    // 実際のアプリでは DB から取得
    const user = await UserRepository.findById(id);
    done(null, user);
  } catch (err) {
    done(err);
  }
});

4. req.isAuthenticated() / req.isUnauthenticated() — 認証状態の確認

リクエストが認証済みかどうかを判定します。認証ガードの実装に使います。

// 認証ガードミドルウェア
function ensureAuthenticated(
  req: Request,
  res: Response,
  next: NextFunction
): void {
  if (req.isAuthenticated()) {
    return next();
  }
  res.status(401).json({ message: '認証が必要です' });
}

// ルートに適用
app.get('/api/profile', ensureAuthenticated, (req: Request, res: Response) => {
  res.json(req.user);
});

5. req.logIn() / req.logOut() — 手動でのログイン・ログアウト

passport.authenticate() を使わずに、手動でセッションを操作する場合に使います。

// 手動ログイン(ユーザー登録直後に自動ログインさせる場合など)
app.post('/register', async (req: Request, res: Response, next: NextFunction) => {
  const newUser = await UserRepository.create(req.body);

  req.logIn(newUser, (err) => {
    if (err) return next(err);
    res.redirect('/dashboard');
  });
});

// ログアウト(v0.6.0 以降はコールバック必須)
app.post('/logout', (req: Request, res: Response, next: NextFunction) => {
  req.logOut((err) => {
    if (err) return next(err);
    res.redirect('/');
  });
});

類似パッケージとの比較

特徴passportexpress-jwtAuth.js (NextAuth)lucia
対応フレームワークExpress / ConnectExpressNext.js / SvelteKit 等フレームワーク非依存
認証方式480+ ストラテジーJWT のみOAuth / Email 等セッションベース
セッション管理あり(要設定)なし(ステートレス)組み込み組み込み
DB 抽象化なし(自由)なしアダプター方式アダプター方式
TypeScript サポート@types/passport組み込み組み込み組み込み
学習コスト低〜中
柔軟性非常に高い低い高い

補足: Passport は歴史が長く情報量が豊富ですが、設計がコールバック時代のものです。新規プロジェクトで Next.js や SvelteKit を使う場合は Auth.js、より現代的なセッション管理が欲しい場合は Lucia も検討する価値があります。

注意点・Tips

1. ミドルウェアの登録順序に注意

passport.initialize()passport.session() は、必ず express-session後に登録してください。順序を間違えるとセッションが機能しません。

// ✅ 正しい順序
app.use(session({ /* ... */ }));
app.use(passport.initialize());
app.use(passport.session());

// ❌ 間違い — session より先に passport を登録
app.use(passport.initialize());
app.use(session({ /* ... */ }));

2. req.logout() にはコールバックが必須(v0.6.0 以降)

v0.6.0 から req.logout() が非同期になりました。コールバックを渡さないとエラーになります。

// ❌ v0.6.0 以降ではエラー
req.logout();

// ✅ コールバックを渡す
req.logout((err) => {
  if (err) return next(err);
  res.redirect('/');
});

3. TypeScript で req.user の型を拡張する

デフォルトでは req.userExpress.User 型です。自分のユーザー型を使うには型定義を拡張します。

// types/express.d.ts
declare global {
  namespace Express {
    interface User {
      id: number;
      username: string;
      email: string;
    }
  }
}
export {};

4. セッションを使わない API サーバーの場合

JWT 認証など、セッション不要な場合は session: false を指定します。

app.get(
  '/api/data',
  passport.authenticate('jwt', { session: false }),
  (req: Request, res: Response) => {
    res.json(req.user);
  }
);

5. 本番環境でのセッションストア

デフォルトの MemoryStore は本番環境には不向きです。Redis や DB ベースのストアを使いましょう。

import RedisStore from 'connect-redis';
import { createClient } from 'redis';

const redisClient = createClient();
await redisClient.connect();

app.use(
  session({
    store: new RedisStore({ client: redisClient }),
    secret: process.env.SESSION_SECRET!,
    resave: false,
    saveUninitialized: false,
  })
);

まとめ

Passport は Node.js エコシステムにおける認証のデファクトスタンダードであり、480 以上のストラテジーによってほぼあらゆる認証方式に対応できます。ストラテジーの登録、シリアライズ/デシリアライズの設定、ミドルウェアの登録順序という 3 つのポイントを押さえれば、柔軟かつ堅牢な認証基盤を構築できます。設計はやや古典的ですが、その分枯れており、Express ベースのアプリケーションでは今なお有力な選択肢です。

比較記事