d9f0fe11c6
- 新增三個 refresh 端點(member/provider/admin),revoke 舊 token 發行新 7 天 token - axios.js / coachAxios.js:401 interceptor 改為 refresh-then-retry,含 isRefreshing queue 防並發、isRedirecting flag 防雙重 redirect - auth.js / coachAuth.js / AuthCallbackView.vue:localStorage 全改為 sessionStorage - echo.js / notificationAxios.js:同步改為 sessionStorage - notifications.js:401 時停止 polling(stopPolling),不印 error log - 新增 TokenRefreshTest.php(11 tests / 24 assertions):refresh 成功/失效/跨角色 403 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.4 KiB
3.4 KiB
1. 後端 Refresh 端點
- 1.1 [後端] 在
app/Http/Controllers/API/AuthController.php新增refreshMember(Request $request):驗證 role=member,revoke 當前 token,發行新 token,回傳{ status: true, data: { token, token_type: "Bearer" } } - 1.2 [後端] 同上新增
refreshProvider(Request $request):驗證 role=provider,revoke + 發新 token - 1.3 [後端] 同上新增
refreshAdmin(Request $request):驗證 role=admin,revoke + 發新 token - 1.4 [後端] 在
routes/api.php新增三個 refresh 路由(需 auth:sanctum middleware):POST /api/member/refresh、POST /api/provider/refresh、POST /api/admin/refresh - 1.5 [後端] 執行
php artisan route:clear確認路由生效
2. 前端 Interceptor 改寫(Member)
- 2.1 [前端] 改寫
frontend/src/api/axios.js:response interceptor 改為 refresh-then-retry 邏輯,加入isRefreshingflag 與pendingRequestsqueue 防止並發重複 refresh - 2.2 [前端] 確認
axios.js的 request interceptor 改從sessionStorage讀取token(非 localStorage)
3. 前端 Interceptor 改寫(Coach)
- 3.1 [前端] 改寫
frontend/src/api/coachAxios.js:response interceptor 改為 refresh-then-retry,呼叫POST /api/provider/refresh,含isRefreshing+ queue 邏輯 - 3.2 [前端] 確認
coachAxios.js的 request interceptor 改從sessionStorage讀取coach_token
4. 前端 Store 改為 sessionStorage
- 4.1 [前端] 修改
frontend/src/stores/auth.js:init()、setAuth()、logout()中所有localStorage改為sessionStorage(keys:token、user) - 4.2 [前端] 修改
frontend/src/stores/coachAuth.js:同上,keys:coach_token、coach_user - 4.3 [前端] 修改
frontend/src/views/AuthCallbackView.vue:第 21 行localStorage.setItem('token', token)改為sessionStorage.setItem('token', token)
5. 自動化測試
- 5.1 [測試] 新增
tests/Feature/TokenRefreshTest.php:驗證POST /api/member/refresh以有效 token 回傳新 token,且舊 token 同時失效(再打一次原 token 回 401) - 5.2 [測試] 同檔案補充:
/api/provider/refresh和/api/admin/refresh相同行為;跨角色呼叫(member token 打 provider/refresh)回 403 - 5.3 [測試] 同檔案補充:過期 token 呼叫 refresh 回 401;已 revoke token 呼叫 refresh 回 401
6. 手動驗證
- 6.1 [整合測試] Token 過期行為確認:將
expires_at改成過去時間,打 API 確認被踢回/login(refresh 端點同樣受 auth:sanctum 保護,過期 token 無法 refresh,此為預期行為。Sliding window 由 7 天有效期自然實現,主動使用期間不會到期) - 6.2 [整合測試] 多並發 401 只 refresh 一次:DevTools Network 確認只有一個
/refresh請求,其他請求等待後以新 token 完成 - 6.3 [整合測試] Refresh 失敗全部請求正確 reject:revoke 所有 token 後觸發 API,確認被踢回登入頁且沒有無限 loop
- 6.4 [整合測試] OAuth callback 改為 sessionStorage:Google 登入後用 DevTools Application 確認 token 在 sessionStorage(非 localStorage)
- 6.5 [整合測試] 關閉分頁後 token 消失:登入後關閉分頁重開,確認 sessionStorage 已清空需重新登入
- 6.6 [整合測試] Role endpoint 對應正確:member token 打
/api/provider/refresh回 403,不會混用