3秒でわかる
各リクエストの手前でトークンを検証し、通過の可否を決める共通処理。ルートごとに認証コードを書かずに保護範囲をまとめて管理できます。
もう少し詳しく
どういうものか
Express などのフレームワークでは、ルートの処理に入る前に通す関数をミドルウェアと呼びます。そのうち認証を担当するものが authMiddleware です。リクエストヘッダーからトークンを取り出して検証し、正しければ利用者情報を req に載せて次へ進め、不正なら401を返してそこで打ち切ります。
検証の中身は JWT の署名確認が代表的ですが、セッション ID を照合する形でも API キーを見る形でも、置き場所と役割は同じです。
なぜ必要か
保護したいエンドポイントが20個あるとき、それぞれの関数の先頭に同じ検証コードを書くと、20か所の重複ができます。1か所だけ書き忘れれば、そのエンドポイントは誰でも叩けます。認証の抜けは見た目には現れないため、テストを書いていなければ気づけません。
共通のミドルウェアに集約しておけば、検証ロジックは1つで済み、どのルートを保護しているかも登録部分を見れば分かります。
具体例
const jwt = require("jsonwebtoken");
function authMiddleware(req, res, next) {
const header = req.headers.authorization || "";
const token = header.startsWith("Bearer ") ? header.slice(7) : null;
if (!token) {
return res.status(401).json({ error: "token required" });
}
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
req.user = { id: payload.sub, role: payload.role };
next(); // 次の処理へ進める
} catch (e) {
return res.status(401).json({ error: "invalid token" });
}
}
app.get("/me", authMiddleware, (req, res) => res.json(req.user));
app.use("/admin", authMiddleware, adminRouter); // 配下すべてを保護つまずきやすいところ
next() の呼び忘れが最も多い不具合です。エラーも出ずにリクエストが返ってこなくなり、フロント側では読み込み中のまま固まります。検証を通した経路すべてで next() に到達するかを確認します。
登録の順番も効きます。app.use(authMiddleware) をルート定義より後に書くと、先に定義したルートには適用されません。ログイン用のエンドポイント自体を保護してしまい、誰もログインできなくなる事故もこの並びの間違いから起きます。
jwt.decode と jwt.verify の取り違えも危険です。前者は署名を確認せず中身を読むだけなので、攻撃者が自作したトークンをそのまま信用します。検証には必ず verify を使います。
401 と 403 の使い分けも整理しておきます。誰か分からないなら401、誰かは分かるが権限が足りないなら403です。権限の判定は認証とは別のミドルウェアに切り出すと、役割が混ざりません。