JWT 指南

JWT 過期後,我如何用正確的 refresh / reauthentication 流程恢復

expired JWT 不應靠修改 exp 或關閉 expiration validation 修復。我會檢查 clock、access/refresh token、stale state、rotation 與正式 reauthentication 流程。

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

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

exp → refresh / reauth → new JWT
exp → refresh / reauth → new JWT

看到 token expired 時,我不會修改 payload 裡的 exp、重新 Base64URL encode,或把 server 的 expiration check 關掉。signed JWT 的 header/payload 一旦改變,原本 signature 就不再對應;而停用 expiration 只是把 credential lifecycle 的安全邊界拿掉。

我的處理方式是:先確認真的過期,再走系統原本設計的 refresh 或重新登入流程。

先確認 exp 與目前時間

我會用 JWT Expiry Checker 查看 exp、iat、nbf。

JavaScript:

const expired = Date.now() >= payload.exp * 1000;

記得 JWT NumericDate 是秒、Date.now() 是毫秒。

如果剛取得的 token 馬上被判定過期,我會先檢查 seconds/milliseconds 與 server clock,而不是立刻認定 issuer 發錯 token。

Access token 與 refresh token 是不同 credential

常見 OAuth 流程中:

  • access token 用來呼叫 resource API;
  • refresh token 用來向 token endpoint 換新的 access token。

refresh token 本身不一定是 JWT。即使它長得像 JWT,我也會依 protocol/provider 定義確認用途。

正常 refresh 流程

概念上:

expired access token
      ↓
valid refresh token
      ↓
token endpoint
      ↓
new access token

如果 refresh token 也失效、過期或被撤銷,就應回到 reauthentication,而不是放寬 verifier。

Refresh 成功卻一直 401,我找 stale state

這是很常見的 application bug:新 token 已拿到,但 API client 還在送舊的那一顆。

我會檢查:

  • request interceptor 是否 capture 舊 token;
  • React/store state 是否更新;
  • 多 browser tab 是否不同步;
  • server session/cache 是否還保留舊值;
  • retry logic 是否重送原 request;
  • service worker 是否影響 request。

最直接的方法是比較 Network 面板裡真正送出的 Bearer token exp,和 refresh response 的新 token 是否一致。

Refresh token rotation 也會造成下一次失敗

一些 provider 使用 refresh token rotation:每次 refresh 都回傳新的 refresh token,舊的可能立即失效。

我會確認:

  • response 是否含新的 refresh token;
  • application 是否 atomic 更新;
  • concurrent refresh 是否互相覆蓋;
  • reuse detection 是否被觸發。

如果只更新 access token、沒更新 rotated refresh token,下一輪 refresh 很可能失敗。

Client 不應單獨決定 security expiration

前端先看 exp 可以改善 UX,例如提早 refresh,但 resource server 仍必須自己驗證 expiration。使用者可以修改 browser clock 或 client code,所以 client-side check 不是 security boundary。

Token 沒過期仍 401 怎麼辦

那就不再把問題叫做「expired token」。我會繼續檢查:

  • signature;
  • issuer;
  • audience;
  • nbf;
  • token type;
  • scope/role;
  • JWKS key rotation/cache。

完整流程見 JWT 401 Unauthorized。

Rotation / revocation 與 expiration 不一樣

即使 exp 還在未來,token 也可能因為:

  • signing key 被 emergency revoke;
  • server-side session 被關閉;
  • denylist/revocation policy;
  • refresh token family 被撤銷;

而提前失效。

反過來,JWT 自帶 exp 也不代表 logout 一定能立即使所有 token 失效,這取決於系統是否有額外 revocation model。

我不採用的修法

  • ignoreExpiration=true 留在 production;
  • 修改 exp 後繼續使用原 signature;
  • 因為 refresh 偶爾失敗就把 access token lifetime 無限拉長;
  • 將長效 refresh token 隨意存進可被 XSS 讀取的 storage;
  • 每個 401 都無限 refresh,造成 retry loop。

我會設計 refresh failure boundary

如果 refresh 失敗,我希望流程有清楚結束條件:

API 401
→ 判斷是否因 access token expiry
→ 最多一次受控 refresh
→ retry original request
→ 若仍失敗,回到登入/顯示錯誤

我不允許 recursive refresh loop。

我的 expired token 清單

  1. exp 是 number 且以秒解讀嗎?
  2. issuer/verifier clock 正常嗎?
  3. 這是 access token 還是其他 token?
  4. 系統是否支援 refresh?
  5. refresh token 是否仍有效?
  6. refresh rotation 是否正確保存新 token?
  7. 新 access token 是否真的被後續 request 使用?
  8. 若仍 401,是否其實是 signature/issuer/audience 問題?
  9. expiration validation 是否仍在 server 強制執行?

我處理 expired JWT 的目標不是「把舊 token 救回來」,而是確認 整個 credential lifecycle——發行、到期、refresh、撤銷、重新登入——都按原本安全設計運作。

來源與參考資料

相關指南