這篇是最近在想 OAuth 2 token 更新策略的一些思考,篇名曰「討論」,因為自己也不確定這些想法是不是所謂的最佳實踐,歡迎讀者留言討論。

在 OAuth 2 的各種 flow 中,最終大多會取得兩種 token,access token 和 refresh token,作為代表用戶的令牌存取服務及資源,這兩種 token 的特性如下:

Access Token

  • 代表用戶存取服務。
  • 有效期短。
  • 因為有效期短,比較不用擔心被竊,一般可以存放在 cookie、local storage、session storage、記憶體等處。

Refresh Token

  • 用於更新 access token 和 refresh token。
  • 除了更新 token 外無法用於存取服務。
  • 有效期長。
  • 因為有效期長,需要較妥善的保存,一般建議存放在具有 HttpOnly 屬性的 cookie 內,但也還是有些人放在 local storage。

Token 更新

因為 access token 效期短,為了塑造良好的用戶體驗,不要讓用戶頻繁登入,我們必須在客端 app 上利用 refresh token 更新 token,一般而言,更新 token 時不僅會取得新的 access token,也會取得新的 refresh token。

基於前面的條件,我們可以設想出第一版的更新策略。

更新策略第一版

OAuth 2 Token 更新

說明如下:

  1. 用戶點了連結或按了某個按鈕。
  2. 進入藍色區域,檢查 access token 效期,如果一切順列,直接進入綠色方塊,拿 access token 向服務端要內容或執行某些事務。
  3. 如果客端根本就沒有 access token,那就進入黃色區域,拿 refresh token 換回一對新的 access token 和 refresh token。
  4. 如果客端的有 access token,但卻過期了,那一樣拿 refresh token 去換一對新的回來。
  5. 如果 refresh token 失敗,表示 refresh token 也是過期的,那就只好請用戶重新登入。
  6. 如果客端自己知道 refresh token 過期,那也是請用戶重新登入。

在這看似有點亂又不太亂的策略中,客端 app 需要管理 access token 和 refresh token 的狀態,也就是知道他們的有效期,但還記得嗎,refresh token 是 HttpOnly 的 cookie,JS 讀不到。

況且不論我知不知道 refresh token 的有效與否,都得走入「Refresh token」這一步,只差在是我主動發現 refresh token 過期而導向登入頁,或是直接走進「Refresh token」方塊,失敗再導向登入頁。站在用戶的角度,他是感受不到這兩條路的差異的,一切都只發生在主客端程式的對話中,基於以上,我們可以把黃色區域整塊拿掉:

OAuth 2 Token 更新

於是簡化成第二版。

更新策略第二版

OAuth 2 Token 更新

第二版清爽多了,也減少客端 app 的邏輯,不用去管 refresh token 還有沒有,只管 access token 存不存在、有沒有過期, 不存在、過期了,就去「Refresh token」方塊跑一下,成功就成功,失敗就帶用戶重新登入。

同樣的,如果藍色方塊有問題都跑去「Refresh token」,那何不連藍色方塊都省掉:

OAuth 2 Token 更新

於是我們有了第三版。

更新策略第三版

OAuth 2 Token 更新

第三版的邏輯不由分說,無須任何判斷 token 存在、有效的邏輯,不關心狀態,只無腦更新,錯了就抓用戶去重新登入。

第三版夠傻瓜也夠聰明,但未必可行,紅色方塊右上的小圖示說明他們是要走網路的,無腦更新,表示服務端必須回應,如果客端 app 人很多,那相當於對服務端發動 DDoS 攻擊,要嘛就是服務端被打爆,要嘛就是用戶被封鎖。

這種作法主客端交互開銷巨大,對服務端來說,要考慮的是 token 的生成效率,token 的主流選擇是 JWT,JWT 是以 Base64 和 HS256 為基礎算出來的,也就是說服務端需要高效率的 Base64 與 HS256 解編碼演算方案,每種語言都有多樣的 Base64、HS256 編解碼器,也可以利用 APM 監控 refresh token 端點的負載,再視需要做優化。

Access Token 悖論

同場加映 access token 悖論。

前面提過 accsss token 有效期短,較能承受外流的風險,例如一個效期僅一分鐘的 access token,就算外流了,多半也不太恐慌,因為一分鐘後失效了,反之,另一個效期短達一天的 access token 外流,那我們可能會緊張一下。

基於上述的設定,我們可以認定效期越短,安心感越大,因此想要安心感無限大,那效期就要無限短,短到趨近於零,但這樣就失去它的功能了,或許我們可以改用「次數」的觀點看效期,從原本的時間效期改為次數效期,也就是所謂的 one-time token,但 access token 還有一個無狀態的特性,該如何無須管理它的狀態又讓它僅能使用單次,答案或許是類似區塊鍊的技術。