Files
CFDivePlatform/openspec/specs/oauth-state-validation/spec.md
T
a620906209 0cd1047b10
Run Tests / test (pull_request) Successful in 30s
docs(openspec): 補齊所有 spec 缺少的 Purpose section 與規格標頭
repo 內 35/38 份既有 spec 都是舊版歸檔流程留下的原始 delta 內容(`##
ADDED Requirements`,缺標題與 Purpose),openspec CLI 現版本嚴格驗證
(--strict)會報錯或警告。逐一補上:

- 缺標題/Purpose 的 30 份:加上 `# <name> Specification` 標題 + 依內容
  撰寫的 Purpose 段落,`## ADDED Requirements` 改回 `## Requirements`
- compose-cloud-baseline、scheduler-container:已有標題與
  Requirements,只補 Purpose 包裝
- provider-verification:已有標題與 Purpose,只需把
  `## ADDED Requirements` 改回 `## Requirements`
- admin-user-management、notification-email:各有一條 requirement 缺
  SHALL/MUST 關鍵字(純敘述句或表格),補上規範用語,內容不變

`openspec validate --specs --strict`:38/38 全過(原本 4/38)。純文件
補齊,未變更任何 requirement 的實質內容或行為描述。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011LpcY9b7y9x4fusHBTXciQ
2026-08-03 04:11:06 +08:00

3.1 KiB
Raw Blame History

oauth-state-validation Specification

Purpose

定義 Google OAuth 登入流程以 state 參數防範 CSRF 攻擊的產生、比對、單次使用與失敗導向規則。

Requirements

Requirement: OAuth 授權流程帶 state 參數防 CSRF

後端 SHALL 在 OAuth redirect 前產生隨機 state 字串(至少 32 bytes hex),存入 session('oauth_state'),並附加於 OAuth redirect URL 的 state query parameter。stateless() 呼叫 SHALL 被移除;Socialite 在 redirect() 內會自行寫入 session('state'),實作 SHALL 在 redirect() 呼叫後以同一 state 值覆蓋 session('state'),確保 Socialite 內建驗證與手動驗證使用相同值。callback 時 SHALL 從 request 讀取 state 並與 session('oauth_state') 比對(hash_equals);比對成功後 SHALL 立即清除;不符或缺少時 SHALL redirect 至 /login?error=oauth_failed

Scenario: 正常 OAuth 登入流程

  • WHEN 使用者點擊「Google 登入」,後端發出 redirect
  • THEN redirect URL 包含 state=<random> 參數,且相同 random 值存入 session;使用者在 Google 完成授權後,callback 帶回相同 state,後端驗證通過,繼續正常登入流程

Scenario: state 缺失(直接訪問 callback

  • WHEN 攻擊者或使用者直接訪問 /auth/google/callback 而不帶 state 參數
  • THEN 後端 redirect 瀏覽器至 {frontend_url}/login?error=oauth_failed,不繼續登入流程

Scenario: state 不符(CSRF 攻擊)

  • WHEN callback 帶的 statesession('oauth_state') 中的值不一致
  • THEN 後端 redirect 瀏覽器至 {frontend_url}/login?error=oauth_failed,不繼續登入流程

Scenario: state 只能使用一次

  • WHEN callback 驗證 state 成功後(session()->pull('oauth_state') 已清除),再次以相同 state 訪問 callback
  • THEN session 中已無 oauth_state,視為 state 缺失,同樣 redirect 至 /login?error=oauth_failed

Scenario: 登入頁顯示 OAuth 失敗訊息

  • WHEN 瀏覽器被 redirect 至 /login?error=oauth_failed
  • THEN LoginView.vue 偵測到 route.query.error === 'oauth_failed',顯示「OAuth 授權失敗,請重新嘗試」提示;AuthCallbackView.vue 不涉及此錯誤流程

Scenario: 多分頁並發 OAuth flow — 只有最後啟動的成功(預期行為)

  • WHEN 使用者在同一瀏覽器開兩個 tab,Tab A 先啟動 OAuthsession 寫入 state_A),Tab B 後啟動 OAuthsession 覆寫為 state_B),Tab A 的 callback 先到達後端
  • THEN Tab A 的 callback 帶 state_A,與 session 中的 state_B 不符,後端 redirect 至 /login?error=oauth_failedTab B 的 callback 帶 state_B,比對成功,正常完成登入

Requirement: OAuth session driver 正常運作

後端 SHALL 確保 session driver 在 Docker 環境中可寫入(storage/framework/sessions 目錄存在且有寫入權限),以支援 state 存取。

Scenario: Session 寫入成功

  • WHEN OAuth redirect 觸發時
  • THEN state 值成功寫入 session,無 500 錯誤