後端面試:如何設計 OAuth 2.0 Step-Up Authentication Challenge?
題幹與適用場景
一個資源伺服器允許普通 access token 讀取資料,但轉帳、修改收款帳戶與匯出敏感資料必須具備更高認證等級。用戶端未滿足要求時,應得到可執行的 challenge,而不是模糊的 403。請根據 RFC 9470 設計資源伺服器、授權伺服器與用戶端之間的 Step-Up 流程。
RFC 9470 為 Bearer challenge 定義 insufficientuserauthentication 錯誤,並以 acr_values 或相關認證情境表達所需等級。題目考察 API 錯誤、權杖 claim、重新授權、重試冪等與風險降級的完整閉環。
面試官考察點
- 能否區分資源授權不足、使用者認證等級不足與權杖過期。
- 能否在 WWW-Authenticate challenge 表達認證情境而不洩露風控細節。
- 能否讓用戶端依 challenge 重新授權,並防止無限迴圈與參數降級。
- 能否在資源伺服器驗證
acr、amr、issuer、audience、scope 與時間 claim。 - 能否處理轉帳重試、並發、稽核、回復與使用者體驗。
回答前需要釐清的問題
- 哪些資源操作需要更高等級?要求
acr、具體amr,還是一次性交易確認? - access token 是 JWT 本地驗證還是 introspection?資源伺服器能否取得認證情境?
- 用戶端是 Web、行動端或伺服器應用?是否啟用 PKCE、prompt 與 max_age?
- challenge 是否只允許提升一次並綁定金額、收款人與 nonce?
- 認證服務暫時不可用時,哪些讀操作可繼續,哪些寫操作必須失敗關閉?
30 秒回答框架
資源伺服器先驗證 token 的 issuer、audience、簽章、過期時間與 scope;若 scope 足夠但認證情境不足,回傳 401 與 WWW-Authenticate challenge,聲明需要的 acr_values 與資源。用戶端保存原始意圖與 state,透過授權端點重新認證,取得綁定同一 audience、scope 與情境的 token,再重試原請求。伺服器限制 challenge TTL、重試次數與交易綁定;高風險操作不靜默降級。
分步驟深入解答
1. 建立認證等級與資源策略
把資源操作映射到認證策略,例如普通讀取要求基礎等級,修改收款帳戶要求抗釣魚認證,轉帳還要交易確認。策略可按 endpoint、方法、租戶、金額與風險訊號計算,不能把所有寫請求簡化成固定等級。
授權伺服器定義可識別的 acr 值與允許的認證方法集合。資源伺服器只接受註冊表中的值,不能讓用戶端任意宣稱更高等級;amr 是證據,不能取代策略對 acr 的判斷。
2. 設計 challenge 回應
當 token 有效但使用者認證不足時,資源伺服器回傳 401,並在 WWW-Authenticate 使用 Bearer challenge 的 error="insufficientuserauthentication"。challenge 可包含要求的 acr_values、資源識別與錯誤 URI,但不能放入帳戶號、風控分數或內部規則。
若 token 沒有認證情境,或情境無法可信驗證,也按不足處理。token 過期、issuer 不可信、audience 不符與 scope 缺失應使用對應錯誤,不把所有失敗偽裝成 step-up。
3. 讓用戶端重新授權
用戶端解析 challenge 後,將原始目標、所需 acr_values、資源參數與 PKCE 綁定到新的 authorization request。Web 用戶端使用 state,OIDC 使用 nonce;行動端避免把 challenge 直接當成可執行 URL。
重新授權必須經過授權伺服器的使用者認證與同意。prompt、max_age 或認證策略只是請求,最終等級由授權伺服器依實際認證結果寫入 token。用戶端不能在本地把 token 標記為已升級。
4. 驗證新 token 與原請求
資源伺服器驗證新 token 的簽章、issuer、audience、scope、exp、iat、acr 與必要的 amr。若使用 introspection,回應必須來自可信授權伺服器並包含足夠情境。不能只比較 scope,因為權限範圍與認證強度是不同維度。
高風險操作把 challenge nonce、交易摘要或授權請求 ID 與 token 或伺服器狀態關聯,避免使用者完成一次高等級認證後把 token 換用於另一筆交易。token audience 要精確綁定目標 API。
5. 防止迴圈、重播與降級
伺服器給 challenge 短 TTL 與唯一 ID,記錄原始請求摘要、租戶、資源、要求等級與重試次數。用戶端最多重試一次或有限次數;同一 challenge 成功後標記消費,失敗與過期都終止。
拒絕用戶端把 acr_values 改成較低等級,拒絕更換資源或 audience,也不因 step-up 逾時就回退普通 token。重複轉帳使用冪等鍵,避免認證重試造成重複副作用。
6. 處理可用性與錯誤邊界
授權伺服器或 introspection 不可用時,普通只讀請求可依短期快取繼續;寫入、支付與權限變更預設失敗關閉。challenge 錯誤不應暴露內部認證方法或帳戶狀態,日誌使用 challenge ID、issuer、結果與延遲。
指標包括情境不足比例、challenge 完成率、迴圈次數、過期、重播、PKCE 失敗、按 acr 拒絕率與業務副作用重複率。告警按資源與租戶切分,區分策略錯誤與攻擊。
7. 灰度與回復
先對單一 API 與租戶啟用,比較 401、完成率、延遲、客服回饋與高風險操作拒絕率。舊用戶端不理解 challenge 時,回傳文件化錯誤並逐步更新 SDK,不能為所有用戶端靜默放開普通 token。
回復只關閉尚未啟用的資源策略,保留既有交易的 challenge 狀態與稽核。若發生 token 情境洩露、重複副作用或 challenge 迴圈,暫停放量、撤銷相關策略並重新驗證權杖綁定。
高品質示範回答
我會先為各類操作定義最小認證等級。資源伺服器驗證 token 後,若 scope 足夠但 acr 或 amr 不符合,就回傳 401 與 insufficientuserauthentication challenge,帶上最小化的 acr_values、資源與錯誤 URI。用戶端保存原始意圖,透過授權端點使用 state、nonce 與 PKCE 重新認證,授權伺服器依真實認證結果簽發新 token。
資源伺服器再次驗證新 token 的 issuer、audience、scope、時間、acr 與必要 amr,並將 challenge ID 或交易摘要綁定伺服器狀態。challenge 使用短 TTL、一次性消費與有限重試,禁止降低等級、替換 audience 或無條件回退。高風險寫操作在認證服務故障時失敗關閉,轉帳使用冪等鍵避免重試副作用。
常見錯誤
- 用 403 或泛化 401 取代可執行的
insufficientuserauthenticationchallenge。 - 只檢查 scope,不檢查 issuer、audience、acr、amr 與時間 claim。
- 讓用戶端自行宣稱更高 acr,或降低
acr_values後繼續。 - 把 challenge 直接當成未驗證授權 URL,忽略 state、nonce 與 PKCE。
- 認證完成後無限重試,或沒有交易綁定導致 token 跨操作重用。
- 認證服務故障時對轉帳與權限變更靜默降級。
- 日誌記錄帳戶、風控分數或完整 token,洩露敏感資訊。
延伸追問與參考答案
scope 足夠但 acr 不足,為什麼仍回傳 401?
scope 表示 token 可存取什麼,acr 表示使用者以什麼認證情境完成流程。資源可能同時要求兩者;認證不足時用戶端需要重新授權。
challenge 應包含哪些欄位?
只包含用戶端可執行所需的認證等級、資源識別、錯誤 URI 與短期 challenge 識別。帳戶、金額、內部風險分數和認證方法細節應留在伺服器。
用戶端能否直接呼叫 token endpoint 要求升級 token?
不能繞過授權伺服器的使用者認證與同意。用戶端應依 challenge 發起授權請求,最終 acr 由授權伺服器依實際認證寫入 token。
如何避免升級後 token 用於另一筆轉帳?
綁定 audience、資源、challenge ID 或交易摘要,並在伺服器保存一次性狀態。交易本身使用冪等鍵,避免重試造成重複副作用。
introspection 暫時不可用怎麼辦?
按資源風險分級。普通讀取可使用短快取;支付、權限變更等操作無法確認認證情境時失敗關閉並告警。
如何相容不理解 challenge 的舊用戶端?
先提供文件化錯誤與 SDK 更新,按租戶灰度啟用。高風險操作不因舊用戶端放寬要求,遷移完成後再強制策略。