在碰陌生的程式碼之前,請 Claude 挖出你不知道自己不知道的事——並告訴你怎麼針對每個缺口改進 prompt。
我要在 Acme 加一個新的 SSO 登入提供者,但我從來沒碰過 auth 模組。幫我做一次盲點掃描:找出我在這部分程式碼裡的未知未知,解釋每一個,並告訴我怎麼改進 prompt 來讓你更好地做實作。
掃描了 services/auth/(47 個檔案),2025 年 1 月以來有 214 個 commit 動過它,3 個進行中的 migration,2 次被 revert 的嘗試。以下是七件你本來會吃虧才學到的事。
「加一個新的 SSO 提供者」——聽起來像:實作一個介面、註冊它、寫 redirect handler,搞定。乾淨的程式碼庫上,兩天的任務。
一個正在做 session storage migration 的模組,有一個 provider 偷偷繞過大家以為一定會跑的 middleware,有一次被 revert 的嘗試就是做你現在要做的事,還有一個身份連結模型從 provider 介面根本看不到。
Acme 正在從 Postgres-backed session 遷移到 Redis(MIG-118,三月起停滯)。目前每次 session 寫入都走 SessionBridge.write(),它會同時寫到 RedisSessionStore 和舊的 pg_sessions 表。遷移從未完成,所以讀取仍然來自 Postgres。
RedisSessionStore——它看起來是正統的,有最新的程式碼和最好的文件。直接寫它,你的登入在 dev 環境會看起來正常(那邊啟用了 Redis 讀取),但在 prod 會默默失敗,因為那邊讀取還是走 Postgres。除了 SAML 之外,所有 provider 都掛在 authPipeline 下面,裡面跑 rate limiting、audit logging 和 device-trust 檢查。saml/handler.ts 的 ACS endpoint 直接掛在 router 上,因為 2024 年 SP-initiated POST callback 打壞了 CSRF 層,後來沒人回頭修。有一行 // TODO(priya): fold back into pipeline 已經 14 個月了。
PR #2841(「新增 Okta OIDC provider」,2025 年 9 月)被 merge 九天後被 revert。revert commit 解釋了原因:Acme 的 callback URL 必須帶 tenant 後綴(/auth/callback/:provider/:workspaceSlug),這樣 workspace 範圍內的邀請連結才能撐過 redirect 來回。Okta 嚴格的 redirect-URI 比對拒絕了動態後綴,被邀請的使用者會落在錯誤的 workspace。大家同意的修法——用 AUTH_STATE_SECRET 簽名的 state 參數 workspace claim——在 revert 討論串裡設計好了,但從沒實作。
Acme 把 users 和 identities 分開:一個帳號可以有多個連結的身份(email+密碼、Google、SAML),用 identities 表串起來。provider 介面只回傳 profile;連結決策發生在 identity/linker.ts,用驗證過的 email 當 key。無法保證 email 已驗證的 provider(很多 SSO IdP 都不保證)必須設 requiresManualLink: true,否則 linker 會開心地把共用公司別名的兩個不同人合併在一起。
Refresh-token rotation 受 flags.yaml 裡的 auth.refresh_rotation 控制。dev 和 staging 預設 true,prod 預設 false(五月一次時鐘偏移事件後暫停 rollout)。flag 關掉時,prod session 剛好活 ACCESS_TOKEN_TTL(12 小時)然後硬過期——沒有 refresh 路徑。
一個 provider 能編譯不代表它就上線了。它必須 (1) 在 providers/registry.ts 註冊,(2) 透過 migration 插入 auth_providers 表——admin UI 讀的是 DB,不是 registry——以及 (3) 加到 __tests__/provider-contract.spec.ts 的 fixture 清單,那裡會對每個註冊的 provider 跑共用 contract suite(state 驗證、replay 保護、logout fan-out)。如果 registry 和 fixture 清單不一致,CI 會失敗。
Session revocation 會在內部 event bus 發布 auth.session.revoked。即時 review player 有自己的長效 playback grant,只有在消費到那個 event 時才會釋放。logout.ts 替現有 provider 處理這件事,但 SSO 單點登出(SLO)callback 走另一條路徑,必須自己發布 event。
幫 Acme 加一個新的 SSO 登入提供者。盲點掃描的限制條件: 1. Session:所有 session 寫入都走 SessionBridge(MIG-118 沒做完; prod 還是從 pg_sessions 讀)。絕不直接碰 RedisSessionStore。 2. 範本:結構以 oauth/google.ts 為準,不要用 saml/handler.ts—— SAML 是故意繞過 authPipeline 的。所有新 route 掛在 authPipeline 裡面,給我看掛載點。 3. 歷史:先讀 PR #2841 的 revert。實作簽名的 state 參數 workspace claim(AUTH_STATE_SECRET),而不是 tenant 後綴 callback URL, 並測試 workspace 邀請流程。 4. 身份連結:在寫程式碼之前先帶我走一遍 identity/linker.ts。如果 這個 IdP 不保證驗證過的 email,設 requiresManualLink 並建立 確認流程——不要自動連結。 5. Flag:以 auth.refresh_rotation=false(prod)來實作。Session 12 小時硬過期;沒有 refresh 路徑。註明如果 rotation 上線要改 什麼。 6. 註冊:三個步驟都做——providers/registry.ts、一個 auth_providers migration、provider-contract fixture 清單。給我看 contract suite 全綠。 7. 登出:每個 revocation 路徑都必須發布 auth.session.revoked, 並加一個測試斷言 IdP 發起的登出會釋放 review player 的 playback grant。 按這個順序做:linker 走讀 → provider skeleton → callback + state claim → 註冊 → SLO + events → contract tests。走讀完之後先停下來 給我看計劃,再寫程式碼。
七句話是你今天早上寫不出來的——每一句都是別人花半天換來的。這就是盲點掃描的意義。