エラーメッセージ
UnauthorizedError: No authorization token was found
別の表記
- No authorization token was found
- Format is Authorization: Bearer [token]
エラーを処理しない場合、Express は「UnauthorizedError: No authorization token was found」で始まるエラーをスタックトレースとともに出力します。`err.code` を見ると、`credentials_required`(トークンなし)と、`credentials_bad_scheme` および `credentials_bad_format`(「Format is Authorization: Bearer [token]」)を区別できます。
express-jwt (Node.js) — UnauthorizedError, code credentials_required
このエラーの意味
express-jwt はルートを保護していますが、トークンを見つけられませんでした。リクエストに `Authorization` ヘッダーがなく、カスタムの `getToken` 関数もトークンを返しませんでした。`credentialsRequired` のデフォルトは `true` なので、ミドルウェアは `code: "credentials_required"` と `status: 401` を持つ `UnauthorizedError` を発生させ、リクエストはハンドラーに届きません。ヘッダーはあるものの `Bearer <token>` の形式になっていない場合は、代わりに「Format is Authorization: Bearer [token]」で失敗します。
発生する理由
クライアントが Authorization ヘッダーを送信していない
よくあるまだログインしていない、ヘッダーを付加するインターセプターのない HTTP クライアントを使っている、期限切れの後にトークンが消去された、といったケースです。ブラウザーのネットワークパネルでリクエストを確認してください。
トークンが Cookie やクエリ文字列で送られている
よくあるexpress-jwt はデフォルトで `Authorization: Bearer` しか読み取りません。HttpOnly Cookie、`?access_token=` パラメーター、カスタムヘッダーに含まれるトークンには `getToken` 関数が必要で、Cookie の場合はさらに `cookie-parser` も必要です。
プロキシがヘッダーを削除した
ときどきヘッダーのマッピングを明示的に設定した API ゲートウェイ、許可リストにあるヘッダーだけを転送するプロキシ、ヘッダーを消去する書き換えルールは、リクエストが Node に届く前に `Authorization` を落とすことがあります。ブラウザーにはヘッダーが表示されていても、サーバーはそれを受け取っていません。
ミドルウェアが公開ルートまで保護している
ときどきルートより前に `app.use(expressjwt(...))` を置くと、ヘルスチェック、ログイン、静的アセットを含むすべてが保護されます。これらのルートへのリクエストにトークンが含まれないのは正当なことです。
修正方法
- 1.
トークンの場所を express-jwt に伝える
`getToken` はリクエストを受け取り、トークン文字列または `null` を返します。シンプルに保ち、複数の場所から同時にトークンを受け付けるのは避けてください。
js import cookieParser from "cookie-parser"; import { expressjwt } from "express-jwt"; app.use(cookieParser()); app.use( expressjwt({ secret: getSigningKey, // e.g. jwks-rsa's expressJwtSecret(...) algorithms: ["RS256"], getToken: (req) => req.cookies.access_token ?? null, }), ); - 2.
公開ルートを除外するか、認証を任意にする
`.unless()` は列挙したパスをスキップします。`credentialsRequired: false` にすると、トークンのないリクエストは `req.auth` なしで通過するため、ルートごとに判断できます。
js app.use( expressjwt({ secret, algorithms: ["HS256"] }).unless({ path: ["/health", "/login", /^\/public\//] }), ); // Or keep authentication optional and decide per route: app.use(expressjwt({ secret, algorithms: ["HS256"], credentialsRequired: false })); - 3.
エラーを適切な 401 に変換する
Express のデフォルトのハンドラーはエラーの `status`(401)を使いますが、HTML ページ(開発環境ではスタックトレースを含む)で応答し、`WWW-Authenticate` ヘッダーを付けません。`UnauthorizedError` を、このヘッダーを付けた JSON の 401 に自分で変換してください。
js app.use((err, req, res, next) => { if (err.name === "UnauthorizedError") { return res.status(401).set("WWW-Authenticate", "Bearer").json({ error: err.code }); } next(err); });