JWT 指南

我如何一步步排查 JWT 401 Unauthorized

JWT 可以正常解碼卻仍然 401 時,我會依序檢查 Authorization header、exp/nbf、signature、issuer、audience、token type、scope 與 key rotation。

作者:JWTDecoder.tools最後確認 2026-09-13

編輯語氣說明:第一人稱敘述代表 JWTDecoder.tools 已文件化的驗證工作流程;發布者與組織作者皆為 JWTDecoder.tools。

JWT 401 除錯流程
依序檢查請求標頭、時間、簽章、issuer、audience、權杖類型與授權條件。

JWT 在 JWT Decoder 裡看起來完全正常,API 卻回 401 Unauthorized,我不會因此認定「JWT 應該是好的」。能 decode 只代表資料可讀,401 可能出在 HTTP 傳輸、時間、signature、issuer、audience 或 authorization policy。

我會固定用同一條路徑排查:

Authorization header
→ JWT 結構
→ exp/nbf/iat
→ signature
→ issuer
→ audience
→ token type
→ scope / role
→ rotation / JWKS cache

1. 先看真正送出去的 HTTP request

我會在 browser network panel、API gateway 或 server request log 確認:

Authorization: Bearer eyJ...

常見問題包括:

  • 少了 Bearer;
  • Bearer 後沒有空白;
  • token 前後多了引號;
  • 送到 API 的其實是 refresh token 或 ID token;
  • frontend refresh 後仍使用舊 token;
  • reverse proxy 把 Authorization header 移除了;
  • staging token 被送到 production API。

先確認傳輸層,常常可以省下很多不必要的 cryptography 排查。

2. 再看 token 是否能正常解析

我會檢查:

  • 是否為預期的 3-part signed JWT;
  • header/payload 是否為有效 JSON;
  • alg、kid;
  • iss、aud;
  • exp、nbf、iat。

即使全部看起來合理,仍然只是 decoded data。

3. 檢查 expiration 與 activation time

我使用 JWT Expiry Checker 看 exp、nbf、iat 與有效區間。

要注意 JWT NumericDate 是秒。如果 exp 已過期,401 很合理;如果 nbf 還在未來,token 也不該被接受。

如果剛登入就顯示過期,我會檢查 issuer/server clock、NTP 與秒/毫秒轉換,而不是把 clock skew 無限放大。

4. 驗證 signature,不只看 kid

HS256

確認 verifier 使用的是正確 shared secret,並確認 secret 的 bytes/encoding 沒被環境變數換行或 Base64 處理改變。

RS256/ES256/PS256

我會走:

expected issuer
→ trusted JWKS
→ kid candidate
→ key compatibility
→ signature verification

JWKS Validator 可以把 key selection 與 signature result 分開顯示。

5. 檢查 iss 是否 exact match

這兩個值可能不同:

https://id.example.com
https://id.example.com/

我不會靠模糊 prefix 或任意 trim 讓它「看起來一樣」。應該讓 provider metadata 與 verifier config 使用同一個 canonical issuer。

完整原則可看 JWT Issuer Validation。

6. 檢查 aud

signature 正確,不代表這個 token 是發給目前 API 的。

常見錯誤是把 ID token 當 access token。ID token 的 audience 可能是 client ID,而 API 預期的是自己的 resource identifier。

aud 也可能是 array,所以 verifier 應判斷 expected audience 是否包含其中,而不是只支援一種資料型別。

可參考 JWT Audience Mismatch。

7. 確認 token type

「長得像 JWT」不是 token type。ID token、access token、內部 assertion 都可能使用 JWT 格式,但用途不同。

我會依 issuer/provider 的 protocol profile 檢查 token 是否真的能用於這個 API。

8. scope / role 也可能是原因

服務不一定嚴格區分 401 與 403。有些 middleware 在權限不足時仍回 401。

所以 signature、issuer、audience 都正常後,我會檢查:

  • required scope;
  • role/permissions;
  • tenant/group custom claim;
  • endpoint policy。

9. Rotation 後只壞新 token,就看 JWKS cache

典型情況:

old kid → 成功
new kid → 401

這時我會查:

  • new public JWK 是否先 publish;
  • verifier 是否還在用 stale JWKS;
  • unknown kid 是否會觸發受控 refresh;
  • old/new key overlap 是否足夠。

詳見 JWT Key Rotation。

10. 比較環境差異

如果 local 成功、production 失敗,我會逐項比較:

  • expected issuer;
  • expected audience;
  • allowed algorithms;
  • JWKS URL;
  • cache 狀態;
  • server clock;
  • proxy header forwarding;
  • required scope/role。

如果兩個 token 很像但結果不同,JWT Diff 可以直接看 header/payload 差異。

我不使用的「快速修復」

  • 關閉 signature verification;
  • 關閉 audience validation;
  • 所有 issuer 都接受;
  • unknown kid 時試遍所有遠端 key;
  • 自動追 token 的 jku;
  • 只要 refresh 一個新 token 就不追 root cause。

這些做法可能讓錯誤消失,但同時讓 trust boundary 消失。

Production log 我記什麼

我避免寫入完整 bearer token。通常記:

  • request/correlation ID;
  • verifier error category;
  • alg、kid;
  • issuer;
  • audience summary;
  • expiration state;
  • JWKS refresh 是否發生。

這些資料已足夠處理大部分 401,而且比儲存完整 credential 安全。

我的 401 最終清單

  1. Authorization header 正確嗎?
  2. 是正確的 access token 嗎?
  3. JWT 結構正常嗎?
  4. exp / nbf / clock 正常嗎?
  5. algorithm 在 allowlist 嗎?
  6. trusted key/JWKS 能驗證 signature 嗎?
  7. iss exact match 嗎?
  8. aud 包含目前 API 嗎?
  9. scope/role 足夠嗎?
  10. rotation/cache 是否只讓新 kid 失敗?

我把 JWT 401 拆成這些階段後,問題就不再是模糊的「token 壞了」,而能定位到 transport、time、cryptographic trust、issuer/audience 或 authorization 的某一層。

來源與參考資料

相關指南