jsonwebtoken の使い方

JSON Web Token implementation (symmetric and asymmetric)

v9.0.3/週MIT認証
AI生成コンテンツ

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

jsonwebtoken の使い方 — Node.jsでJWT認証を実装する

一言でいうと

jsonwebtoken は、Node.js で JSON Web Token(JWT)の生成・検証・デコードを行うための定番ライブラリです。HMAC(対称鍵)と RSA/ECDSA(非対称鍵)の両方に対応し、RFC 7519 に準拠した JWT を扱えます。

どんな時に使う?

  • API認証: Express や Fastify などのバックエンドで、ステートレスなトークンベース認証を実装したい時
  • マイクロサービス間通信: サービス間でユーザー情報や権限を安全に受け渡したい時
  • パスワードリセットやメール認証: 有効期限付きの一時トークンを発行して、リンク経由で本人確認を行いたい時

インストール

# npm
npm install jsonwebtoken

# yarn
yarn add jsonwebtoken

# pnpm
pnpm add jsonwebtoken

TypeScript を使う場合は型定義も追加します。

npm install -D @types/jsonwebtoken

基本的な使い方

最も一般的なパターンは「トークンの発行 → トークンの検証」です。

import jwt from 'jsonwebtoken';

const SECRET = 'your-256-bit-secret';

// 1. トークンを発行する
const token = jwt.sign(
  { userId: 123, role: 'admin' },
  SECRET,
  { expiresIn: '1h' }
);

console.log(token);
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQ...

// 2. トークンを検証する
try {
  const decoded = jwt.verify(token, SECRET);
  console.log(decoded);
  // { userId: 123, role: 'admin', iat: 1700000000, exp: 1700003600 }
} catch (err) {
  console.error('トークンが無効です:', err);
}

よく使うAPI

1. jwt.sign() — トークンの生成

import jwt, { SignOptions } from 'jsonwebtoken';

const SECRET = 'my-secret-key';

// 同期的に生成(HMAC SHA256 がデフォルト)
const token = jwt.sign({ userId: 1 }, SECRET, {
  expiresIn: '2h',       // 有効期限: 2時間
  issuer: 'my-app',      // 発行者
  audience: 'my-client',  // 対象者
  subject: 'auth',        // 用途
} as SignOptions);

// 非同期で生成(コールバック版)
jwt.sign({ userId: 1 }, SECRET, { expiresIn: '2h' }, (err, token) => {
  if (err) {
    console.error('署名に失敗:', err);
    return;
  }
  console.log('生成されたトークン:', token);
});

2. jwt.sign() with RSA — 非対称鍵での署名

import jwt from 'jsonwebtoken';
import fs from 'fs';

const privateKey = fs.readFileSync('private.pem');
const publicKey = fs.readFileSync('public.pem');

// RSA SHA256 で署名
const token = jwt.sign(
  { userId: 42, permissions: ['read', 'write'] },
  privateKey,
  { algorithm: 'RS256', expiresIn: '30m' }
);

// 公開鍵で検証
const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'] });
console.log(decoded);

3. jwt.verify() — トークンの検証

import jwt, { JwtPayload, VerifyOptions } from 'jsonwebtoken';

const SECRET = 'my-secret-key';

function verifyToken(token: string): JwtPayload {
  const options: VerifyOptions = {
    algorithms: ['HS256'],       // 許可するアルゴリズムを明示
    issuer: 'my-app',           // 発行者の一致を検証
    audience: 'my-client',      // 対象者の一致を検証
    clockTolerance: 5,          // 時刻のずれを5秒まで許容
  };

  const decoded = jwt.verify(token, SECRET, options);

  // string が返る場合もあるため型ガード
  if (typeof decoded === 'string') {
    throw new Error('Unexpected token format');
  }

  return decoded;
}

try {
  const payload = verifyToken(token);
  console.log('ユーザーID:', payload.userId);
} catch (err) {
  if (err instanceof jwt.TokenExpiredError) {
    console.error('トークンの有効期限が切れています');
  } else if (err instanceof jwt.JsonWebTokenError) {
    console.error('トークンが不正です:', err.message);
  } else if (err instanceof jwt.NotBeforeError) {
    console.error('トークンはまだ有効ではありません');
  }
}

4. jwt.decode() — 検証なしのデコード

import jwt from 'jsonwebtoken';

// 署名を検証せずにペイロードを読み取る
// ⚠️ 信頼できないトークンの内容を信用してはいけない
const decoded = jwt.decode(token, { complete: true });

console.log(decoded);
// {
//   header: { alg: 'HS256', typ: 'JWT' },
//   payload: { userId: 1, iat: 1700000000, exp: 1700007200 },
//   signature: 'xxxxx'
// }

// ペイロードだけ取得
const payload = jwt.decode(token);
console.log(payload);
// { userId: 1, iat: 1700000000, exp: 1700007200 }

5. Express ミドルウェアとしての実践的な使い方

import express, { Request, Response, NextFunction } from 'express';
import jwt, { JwtPayload } from 'jsonwebtoken';

const SECRET = process.env.JWT_SECRET!;

// カスタム型を拡張
interface AuthRequest extends Request {
  user?: JwtPayload;
}

// 認証ミドルウェア
function authenticate(req: AuthRequest, res: Response, next: NextFunction): void {
  const authHeader = req.headers.authorization;

  if (!authHeader?.startsWith('Bearer ')) {
    res.status(401).json({ error: 'Authorization header missing' });
    return;
  }

  const token = authHeader.split(' ')[1];

  try {
    const decoded = jwt.verify(token, SECRET, {
      algorithms: ['HS256'],
    });

    if (typeof decoded === 'string') {
      res.status(401).json({ error: 'Invalid token format' });
      return;
    }

    req.user = decoded;
    next();
  } catch (err) {
    if (err instanceof jwt.TokenExpiredError) {
      res.status(401).json({ error: 'Token expired' });
    } else {
      res.status(401).json({ error: 'Invalid token' });
    }
  }
}

const app = express();

// ログインエンドポイント
app.post('/login', (req: Request, res: Response) => {
  // 認証ロジック(省略)
  const token = jwt.sign(
    { userId: 1, role: 'admin' },
    SECRET,
    { expiresIn: '1h' }
  );
  res.json({ token });
});

// 保護されたエンドポイント
app.get('/profile', authenticate, (req: AuthRequest, res: Response) => {
  res.json({ user: req.user });
});

類似パッケージとの比較

特徴jsonwebtokenjosefast-jwt
週間DL数約1,800万約1,000万約100万
TypeScript対応@types/jsonwebtoken が必要ネイティブ対応ネイティブ対応
非同期APIコールバック形式Promise / async-awaitPromise / async-await
Web Crypto API対応❌(Node.js専用)✅(ブラウザ・Edge Runtime対応)❌(Node.js専用)
パフォーマンス標準的標準的高速(キャッシュ機構あり)
JWE(暗号化)対応
メンテナンスAuth0が管理、安定活発活発

補足: Next.js の Edge Runtime や Cloudflare Workers で JWT を扱う場合は、Web Crypto API に対応した jose が適しています。Node.js 専用のバックエンドであれば jsonwebtoken で十分です。

注意点・Tips

🔴 jwt.decode() を認証に使わない

decode() は署名を検証しません。認証・認可の判断には必ず verify() を使ってください。

// ❌ 危険: 改ざんされたトークンでも読めてしまう
const payload = jwt.decode(untrustedToken);

// ✅ 安全: 署名が不正なら例外がスローされる
const payload = jwt.verify(untrustedToken, SECRET);

🔴 algorithms オプションを必ず指定する

verify()algorithms を省略すると、デフォルトのアルゴリズムリストが使われます。攻撃者がヘッダーのアルゴリズムを書き換える「Algorithm Confusion Attack」を防ぐため、許可するアルゴリズムを明示しましょう。

// ✅ 許可するアルゴリズムを明示
jwt.verify(token, SECRET, { algorithms: ['HS256'] });

🟡 expiresIn の文字列指定に注意

数値は「秒」として解釈されますが、単位なしの文字列は「ミリ秒」として解釈されます。

jwt.sign(payload, SECRET, { expiresIn: 60 });      // 60秒
jwt.sign(payload, SECRET, { expiresIn: '60' });     // 60ミリ秒 ⚠️
jwt.sign(payload, SECRET, { expiresIn: '60s' });    // 60秒 ✅
jwt.sign(payload, SECRET, { expiresIn: '1h' });     // 1時間 ✅
jwt.sign(payload, SECRET, { expiresIn: '7d' });     // 7日 ✅

🟡 シークレットキーは十分な長さにする

HMAC SHA256 の場合、最低でも256ビット(32バイト)以上のランダムな文字列を使いましょう。

# 安全なシークレットの生成例
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"

🟡 ペイロードに機密情報を入れない

JWT のペイロードは Base64URL エンコードされているだけで、暗号化されていません。パスワードやクレジットカード番号などの機密情報は絶対に含めないでください。

🟢 expexpiresIn を同時に指定しない

ペイロードに直接 exp を含める方法と、オプションの expiresIn を使う方法がありますが、両方を同時に指定するとエラーになります。

// ❌ エラー: 両方指定はできない
jwt.sign({ exp: Math.floor(Date.now() / 1000) + 3600 }, SECRET, { expiresIn: '1h' });

// ✅ どちらか一方を使う
jwt.sign({ data: 'test' }, SECRET, { expiresIn: '1h' });

まとめ

jsonwebtoken は Node.js における JWT 実装のデファクトスタンダードであり、sign() / verify() / decode() の3つの API だけでトークンベース認証を実装できるシンプルさが魅力です。algorithms の明示や decode()verify() の使い分けなど、セキュリティ上の注意点を押さえておけば、安全で堅牢な認証基盤を構築できます。Edge Runtime やブラウザ環境が必要な場合は jose への移行も検討してください。