關於 AuthContext 在登入時會將舊 token 初始為 undefined 問題 #260
Replies: 2 comments 1 reply
-
Hi @ttpss930141011, 謝謝你 trace 了整段 refresh token 流程,但我沒有看到造成 root cause(token 是 undefined)的原因。
延伸議題:如果要防止下一次遇到 token 是 undefined,是不是要寫測試來保證此段邏輯功能正確性? |
Beta Was this translation helpful? Give feedback.
-
Hello 各位, 先整理幾個背景狀況提前說明:
一、為何該 API 會在沒有 token 的情況下發出請求 觀察目前
可以發現是透過 useEffect 呼叫 fetch 函式,該函式先嘗試透過 cookie 取得 jwtToken,若有成功取得,則執行 API,並將回傳結果賦值給 authContext 的 token,倘若失敗則將 token 設為 null。 此處個人的看法是,cookie 中有留存 jwtToken 並不代表 authContext 裡的 token 也一定有值,回顧背景狀況第 2 點,API 夾帶的 token 只會從 authContext 取得,因此這裡尚未針對 「cookie 有存 jwtToken 但 authContext 的 token 不存在」的情境進行例外處理,是造成此 API 帶到 undefined 的第一層原因。 然於背景狀況第 3 點有說明,除執行 authentication API 之外,還有登入這個時機點可以更新 authContext 的 token,邏輯上從登入頁登入後,在 [token] 路由的元件使用 setToken 賦值,再經由 先接續背景狀況第 4 點,本專案有實作讓使用者即使離開網站依然可以保留離開前的 token 的機制,因此造訪網站的使用者不一定會走登入流程來驗證身分並更新 authContext 的 token。 但是,相同的程式碼就會得到相同的結果,依照目前的解讀,在這種情形下,雖然 cookie 保留了 token,但此時 authContext 的 token 尚未有值 (undefined) ,因此請求 authentication API 時,依然會帶到 undefined 的 token,故個人認為解決 API 誤帶 undefined 的議題,可以先從這裡下手。
在 React 中,狀態的更新可能是非同步的 (參考官方範例),在同一個函式使用 setToken,並不會立即讓下一行程式碼訪問到最新的 token ,因此上述程式碼異動的重點是,強制讓此 useEffect 至少執行兩次,第一次嘗試從 cookie 拿到 jwtToken,並使用 setToken 更新狀態,等到第二次進來時,就能保證 token 是更新過後的並依此 token 執行身分驗證,開發模式的結果截圖如下,可以觀察到這次 API 正確帶入了開發模式下模擬的 token: login-token : 上述程式碼尚有未深入思考之處,譬如額外用了一組似乎可避免的狀態來控制流程,以及 useEffect 的依賴陣列沒有如實納入相關變數與函式等等,因此不適合納進主分支作為解決方案,但想說明的是 authentication API 誤帶 undefined token 的議題,是有可能透過調整 二、為何在該時機點 authContext 裡的 token 會是 undefined 這個議題反覆測試了好幾次,目前沒有肯定的答案,只有一些線索可以分享:
目前可能可以從兩個方向測試或釐清,第一是,是否有可能為 Next.js 的 prerender 機制的因素,裡面提到 「By default, Next.js pre-renders every page. This means that Next.js generates HTML for each page in advance, instead of having it all done by client-side JavaScript.」,第二是往前追朔以前的 commit,確認是否在專案之初,每次切換路由就會造成 context們重置。 最後 Justin 提到的 Zustand 狀態管理套件,我覺得可以考慮,原因是個人如果要改寫 以上說明,供參考,謝謝! |
Beta Was this translation helpful? Give feedback.
-
前言
Branch
這次的 branch:
https://github.com/Game-as-a-Service/Game-Lobby-Web/tree/fix/justin-test
情況
搭好,前幾日在 Discord 出現了 token === undefined 的 issue,但在我印象中在登入後 token 就會先被設定一個值,沒道理在 useRequest 時 token 又等於undefined,研究了一下之後發現 AuthContext 在拿到一個預設 token 之後,被設定成 undefined (但並沒有程式碼去setToken(undefined)
Root cause
原先帶有舊 token 的 AuthContext,在 Startup 組件在打出 authenticate request 前被設定成 undefined,導致有此次 issue。
我的猜想
我猜想 AuthContext 被重新初始化了,但我不太清楚為什麼 useContext 會在這個奇怪的時間點進行重新初始化,問了 GPT 後他給我了這個答案:
但是在登入跳轉時應該不會導致 token 丟失,讓我很困惑 useContext 重置的機制,所以特地向各位尋求幫助,但整個問題的前後文很冗長,我嘗試優化了一下內容,希望這樣更清楚明瞭,並請各位大大不吝賜教。
登入流程
由於登入流程比較複雜,大致交代流程:
Development
Production
GET / https://api.gaas.waterballsa.tw/login?type=google-oauth2
並讓 server 轉到 Google 那https://lobby.gaas.waterballsa.tw/auth/token/${token}
POST / authenticate
實作成 protected API 導致 401,無法像在 Development 環境時拿到新 token。Zustand 替換 useContext
在 fix/justin-test 這個 branch 中,我有用一個輕量 state management tool Zustand 去做原先 AuthContext 在做的事情,並把他持久化在 sessionStorage 裡面,解決了上述的 token 變 undefined 的問題:
有興趣的話可以在 hooks\context\useAuth.ts 中將 useContext 替換成註解起來的 useAuthStore 看看效果
想討論的議題
Note: 我先前有實作了 Zustand 整合 Socket.io 的小 demo,用起來差不多像是這樣子:frontend/src/store/socket.ts
Beta Was this translation helpful? Give feedback.
All reactions