koa-passport vs jsonwebtoken vs koa-jwt vs passport-jwt
Node.js における JWT 認証の実装とミドルウェア選定
koa-passportjsonwebtokenkoa-jwtpassport-jwt類似パッケージ:

Node.js における JWT 認証の実装とミドルウェア選定

jsonwebtokenkoa-jwtkoa-passportpassport-jwt は、すべて JSON Web Token (JWT) を扱いますが、役割と抽象化のレベルが異なります。jsonwebtoken は JWT の署名と検証を行う低レベルなコアライブラリです。koa-jwt は Koa フレームワーク専用のミドルウェアで、jsonwebtoken の機能をラップしています。passport-jwt は Passport.js エコシステム向けの JWT 認証戦略であり、koa-passport は Passport を Koa で動作させるためのアダプターです。これらを組み合わせることで、シンプルな認証から複雑なマルチ戦略認証まで実現できます。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
koa-passport227,84077117.1 kB134年前MIT
jsonwebtoken018,19043.4 kB2089ヶ月前MIT
koa-jwt01,35043.2 kB7-MIT
passport-jwt01,97952 kB42-MIT

Node.js における JWT 認証:ライブラリとミドルウェアの徹底比較

JWT(JSON Web Token)を用いた認証は、現代の Web アプリケーションにおいて標準的な仕組みとなっています。しかし、実装方法にはいくつかの選択肢があり、それぞれに適したシナリオが異なります。jsonwebtokenkoa-jwtkoa-passportpassport-jwt の 4 つのパッケージは、一見すると同じ目的のために存在するように見えますが、実際には異なるレイヤーで動作します。

本稿では、これらのパッケージの技術的な違い、実装コスト、そして拡張性の観点から比較し、プロジェクトに最適な選択をするための指針を提供します。

🧱 基本機能:トークンの生成と検証

認証フローの根幹となるのは、トークンの「発行(署名)」と「検証」です。この基本機能を提供しているのは jsonwebtoken だけです。他の 3 つは、この機能をラップしてミドルウェアとして提供しています。

jsonwebtoken

最も低レベルなライブラリです。HTTP リクエストの処理は行わず、純粋なトークン操作に特化しています。

const jwt = require('jsonwebtoken');

// トークンの発行
const token = jwt.sign({ userId: 123 }, 'SECRET_KEY', { expiresIn: '1h' });

// トークンの検証
try {
  const decoded = jwt.verify(token, 'SECRET_KEY');
  console.log(decoded.userId); // 123
} catch (err) {
  console.error('Invalid token');
}

koa-jwt

内部で jsonwebtoken を使用していますが、Koa のミドルウェアとして動作します。リクエストヘッダーからのトークン抽出を自動で行います。

const jwt = require('koa-jwt');

// ミドルウェアとして設定
app.use(jwt({ secret: 'SECRET_KEY' }).unless({ path: ['/public'] }));

// 検証済みのユーザー情報は ctx.state.user に格納される
app.use(async (ctx) => {
  ctx.body = ctx.state.user;
});

passport-jwt

Passport.js の「戦略(Strategy)」として機能します。単体では Koa や Express のミドルウェアにはならず、Passport の初期化プロセス内で定義されます。

const JwtStrategy = require('passport-jwt').Strategy;

// 戦略の定義
passport.use(new JwtStrategy({
    jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
    secretOrKey: 'SECRET_KEY'
  },
  async (jwt_payload, done) => {
    // ユーザー検索ロジック
    const user = await findUser(jwt_payload.id);
    return done(null, user);
  }
));

koa-passport

Passport を Koa で動作させるためのアダプターです。passport-jwt で定義した戦略を、Koa のミドルウェアとして登録するために使用します。

const passport = require('koa-passport');

// Passport の初期化
app.use(passport.initialize());

// ルートでの認証
router.get('/profile', passport.authenticate('jwt', { session: false }), async (ctx) => {
  ctx.body = ctx.state.user;
});

🔄 ミドルウェア統合の仕組み

フレームワークとの統合方法は、開発体験(DX)に大きな影響を与えます。koa-jwt は Koa 専用に設計されているため非常にシンプルですが、passport-jwt + koa-passport の組み合わせは設定が複雑になる代わりに柔軟性を提供します。

koa-jwt:Koa ネイティブな統合

Koa のコンテキスト(ctx)に直接依存しているため、設定が最小限で済みます。Authorization ヘッダーの解析もデフォルトで処理されます。

// koa-jwt の場合
// 1. ミドルウェアを登録するだけ
app.use(koaJwt({ secret: 'KEY' }));

// 2. ctx.state.user ですぐに使える

koa-passport + passport-jwt:アダプターを介した統合

Passport は本来 Express 向けに設計されています。koa-passport が Request/Response オブジェクトを変換して橋渡しを行います。このため、初期化ステップが一つ増えます。

// koa-passport の場合
// 1. 戦略を登録
passport.use('jwt', new JwtStrategy({ ... }, verifyCallback));

// 2. 初期化ミドルウェアを登録
app.use(passport.initialize());

// 3. ルートごとに authenticate を呼ぶ
router.get('/', passport.authenticate('jwt'), handler);

🛡️ 認証フローの制御と柔軟性

プロジェクトの規模が大きくなると、認証フローの細かい制御が必要になることがあります。ここでは、エラーハンドリングやトークン抽出のカスタマイズ性を比較します。

トークン抽出のカスタマイズ

デフォルトでは Authorization: Bearer <token> ヘッダーからトークンを取得しますが、クエリパラメータや Cookie から取得したい場合があります。

jsonwebtoken

完全に手動です。どこからトークンを取得するかは開発者が自由に実装します。

// 手動で Cookie から取得
const token = ctx.cookies.get('access_token');
const user = jwt.verify(token, 'SECRET');

koa-jwt

getToken オプションでカスタマイズ可能です。

app.use(koaJwt({
  secret: 'SECRET',
  getToken: (ctx) => ctx.cookies.get('access_token')
}));

passport-jwt

jwtFromRequest で抽出関数を指定します。 extractor の種類が豊富です。

new JwtStrategy({
  jwtFromRequest: ExtractJwt.fromExtractors([
    ExtractJwt.fromAuthHeaderAsBearerToken(),
    (req) => req.cookies?.access_token // カスタム抽出
  ]),
  secretOrKey: 'SECRET'
}, verifyCallback);

エラーハンドリング

認証失敗時の挙動も重要です。デフォルトでは 401 エラーを返しますが、独自のレスポンスを返したい場合があります。

koa-jwt

unless オプションで除外パスを設定できますが、エラーハンドリングはミドルウェアチェーンで捕捉します。

app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    if (err.status === 401) {
      ctx.status = 401;
      ctx.body = { message: 'カスタムエラーメッセージ' };
    }
  }
});

koa-passport

authenticate オプションでコールバックを渡すことで、失敗時の処理をルーティングレベルで制御できます。

router.get('/protected', passport.authenticate('jwt', { session: false }, (err, user) => {
  if (!user) {
    ctx.status = 401;
    ctx.body = { message: 'カスタムエラーメッセージ' };
    return;
  }
  ctx.state.user = user;
}));

🌐 拡張性とエコシステム

将来的に JWT 以外の認証(Google、GitHub、ローカル認証など)を追加する予定があるかどうかが、選定の決定的な要因になります。

jsonwebtoken / koa-jwt

JWT 専用です。他の認証方式を追加するには、別途ミドルウェアを実装するか、別のライブラリを導入する必要があります。エコシステムは単一目的に特化しています。

passport-jwt / koa-passport

Passport.js エコシステムの一部です。passport-local(ID/ パスワード)や passport-google-oauth などを同じ設定で追加できます。認証戦略が統一されるため、大規模なアプリケーションで管理コストが下がります。

// Passport エコシステムなら複数の戦略を併用可能
passport.use(new LocalStrategy(...));
passport.use(new GoogleStrategy(...));
passport.use(new JwtStrategy(...));

// すべて koa-passport で統一的に扱える
router.post('/login', passport.authenticate('local'));
router.get('/oauth', passport.authenticate('google'));
router.get('/api', passport.authenticate('jwt'));

📊 比較サマリー

機能jsonwebtokenkoa-jwtpassport-jwt + koa-passport
抽象化レベル低(ユーティリティ)中(ミドルウェア)高(フレームワーク + 戦略)
フレームワーク非依存Koa 専用Koa (Passport アダプター経由)
設定の手間手動実装が必要最小限初期化と戦略定義が必要
拡張性低い(JWT のみ)低い(JWT のみ)高い(多様な戦略と併用可能)
エラー制御完全手動ミドルウェアチェーンコールバックで細かく制御
推奨ユースケースカスタム実装、学習用シンプルな Koa API複雑な認証要件、複数戦略

💡 最終的な推奨事項

プロジェクトの要件に応じて、以下のように選択するのが賢明です。

1. シンプルな Koa API サーバーの場合 koa-jwt を選択してください。設定が簡単で、Koa の哲学に合致しています。JWT 認証だけで完結するプロジェクトでは、Over-Engineering(過剰設計)を避けることができます。

2. 複数の認証方式を扱う場合 passport-jwtkoa-passport の組み合わせを選択してください。将来的に OAuth や ID/ パスワード認証を追加する可能性があるなら、最初から Passport エコシステムに載せておくことで、後々のリファクタリングコストを削減できます。

3. 完全な制御が必要な場合 jsonwebtoken を直接使用してください。ミドルウェアの挙動に不満がある場合や、独自の認証プロトコルを構築する場合には、このライブラリが最も柔軟です。ただし、セキュリティ実装の責任はすべて開発者にあります。

結論として、多くの現代的な Koa プロジェクトでは、koa-jwt がバランスの取れた選択です。しかし、エンタープライズレベルの認証要件がある場合は、Passport エコシステムへの投資が長期的なメンテナンス性を高めます — どちらを選ぶにせよ、jsonwebtoken がその根幹にあることを理解しておくことが重要です。

選び方: koa-passport vs jsonwebtoken vs koa-jwt vs passport-jwt

  • koa-passport:

    Koa を使用しつつ、Passport の豊富な戦略(passport-jwt など)を利用したい場合に必須です。Passport の初期化とルーティングを Koa 向けに変換する役割を果たします。

  • jsonwebtoken:

    カスタムミドルウェアを作成したい場合や、フレームワークに依存しない純粋な JWT 操作が必要な場合は jsonwebtoken を選択します。認証フローの每一个细节を制御したい上級者向けです。

  • koa-jwt:

    Koa を使用していて、JWT 認証のみが必要で、追加の認証戦略(OAuth など)を予定していない場合は koa-jwt が最適です。設定がシンプルで、Koa のコンテキストに自然に統合されます。

  • passport-jwt:

    Passport.js のエコシステムを利用する場合、特に JWT 以外の認証戦略(Google ログインなど)と組み合わせたい場合は passport-jwt を選択します。単体ではミドルウェアとして動作しないため、アダプターと併用が必要です。

koa-passport のREADME

koa-passport

Passport middleware for Koa

NPM

koa-passport versionpassport versionkoa versionbranch
6.x, 5.x6.x, 5.x2.xmain
4.x4.x2.xv3.x
3.x, 2.x2.x2.xv2.x
1.x1.x1.xv1.x

Usage

// body parser
const bodyParser = require('koa-bodyparser')
app.use(bodyParser())

// Sessions
const session = require('koa-session')
app.keys = ['secret']
app.use(session({}, app))

const passport = require('koa-passport')
app.use(passport.initialize())
app.use(passport.session())

Example Application

Passport's values and methods are exposed as follows:

app.use(async ctx => {
  ctx.isAuthenticated()
  ctx.isUnauthenticated()
  await ctx.login()
  ctx.logout()
  ctx.state.user
})

License

MIT