security(auth): P0 認證安全強化

- Token 過期時間設為 7 天(sanctum.php expiration)
- prune-expired 每日排程清除過期 token 記錄
- Google OAuth redirect 改用 URL fragment(#token=)避免 token 出現在 server log
- SocialAuthController env() 改 config() 修正 config:cache 下 redirect 失效問題
- 登入端點加 rate limiting:member/provider throttle:5,1,admin throttle:3,1
- axios/coachAxios 加 401 response interceptor,token 過期自動登出
- 修正 migration SQLite 相容性(MODIFY COLUMN),Feature tests 從 7 通過恢復至 41
- 新增 AuthRateLimitTest(5 tests)驗證各角色頻率限制行為

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 01:54:40 +08:00
parent 8d1ea49abc
commit cb9c9a9385
22 changed files with 502 additions and 10 deletions
+16
View File
@@ -13,4 +13,20 @@ api.interceptors.request.use((config) => {
return config
})
api.interceptors.response.use(
(response) => response,
(error) => {
if (
error.response?.status === 401 &&
!error.config.url.includes('/login') &&
!error.config.url.includes('/register')
) {
localStorage.removeItem('token')
localStorage.removeItem('user')
window.location.href = '/login'
}
return Promise.reject(error)
}
)
export default api
+16
View File
@@ -13,4 +13,20 @@ coachApi.interceptors.request.use((config) => {
return config
})
coachApi.interceptors.response.use(
(response) => response,
(error) => {
if (
error.response?.status === 401 &&
!error.config.url.includes('/login') &&
!error.config.url.includes('/register')
) {
localStorage.removeItem('coach_token')
localStorage.removeItem('coach_user')
window.location.href = '/coach/login'
}
return Promise.reject(error)
}
)
export default coachApi
+1 -1
View File
@@ -9,7 +9,7 @@ const route = useRoute()
const auth = useAuthStore()
onMounted(async () => {
const token = route.query.token
const token = new URLSearchParams(window.location.hash.substring(1)).get('token')
const error = route.query.error
if (error || !token) {