具代表性的面試主題

後端面試:如何設計 OAuth 2.0 Device Authorization Grant?

後端困難
Offer.cc 編輯團隊發佈 更新

題幹

請為沒有合適瀏覽器或輸入能力的電視設計 OAuth 2.0 Device Authorization Grant:裝置顯示使用者碼,使用者用手機完成授權,裝置輪詢取得權杖。如何定義介面、狀態機、輪詢策略、過期與安全邊界?

題幹與適用場景

這是 OAuth 協定實作與安全設計題。RFC 8628 面向智慧電視、媒體主機、印表機等無法方便輸入或執行瀏覽器的裝置:裝置向授權伺服器申請 device_codeuser_code,使用者在另一台裝置的瀏覽器中完成登入與授權,原裝置再輪詢權杖端點。裝置通常是 public client,無法把長期客戶端密鑰藏在韌體。題目不要求用 Device Flow 取代能執行系統瀏覽器的原生應用程式授權碼流程。

面試官考察點

  • 能否正確區分裝置碼、使用者碼、驗證 URI、存取權杖與更新權杖的生命週期。
  • 能否把 authorization_pendingslow_downexpired_tokenaccess_denied 映射到狀態機。
  • 能否設計有界輪詢、退避、冪等與並發控制,避免權杖端點被打爆。
  • 能否識別使用者碼釣魚、裝置冒認、暴力猜碼與 public client 風險。

回答前需要釐清的問題

先確認裝置是否能顯示 URI 與使用者碼、是否能發起 HTTPS、是否有可靠時鐘,以及授權伺服器是否支援 OIDC 登入。再確認裝置需要存取哪些資源、權杖是否必須綁定裝置、使用者碼有效期與輪詢間隔由誰配置、是否允許取消。若裝置已有系統瀏覽器,應優先考慮 RFC 8252 的授權碼流程,而非強行使用 Device Flow。

30 秒回答框架

我會實作兩個端點:裝置授權端點回傳短生命週期 device_code、可手動輸入的 user_codeverification_uri、過期時間與最小輪詢間隔;權杖端點只接受 device code 並回傳權杖或標準錯誤。伺服器保存雜湊後的裝置碼、狀態、客戶端與過期時間。裝置依伺服器提供的 interval 輪詢,收到 authorization_pending 繼續,slow_down 增加間隔,成功後停止;過期、拒絕或無效立即終止。使用者頁面顯示正在授權的裝置資訊,降低釣魚風險。

分步驟深入解答

1. 建模參與者與一次授權工作階段

參與者包括受限裝置、授權伺服器、使用者瀏覽器與資源伺服器。裝置授權端點建立一次工作階段,產生高熵隨機 device_code 與便於輸入的 user_code,關聯客戶端、要求範圍、建立時間、過期時間與輪詢狀態。資料庫只保存 device code 的不可逆摘要,避免讀庫洩露即可直接換取權杖;使用者碼需要獨立索引與嚴格嘗試次數。

2. 設計裝置授權回應

回應至少包含 device_codeuser_codeverification_uriexpires_inintervalverification_uri_complete 可提供帶使用者碼的便利連結,但更應在裝置螢幕與使用者頁面同時顯示相同短碼,要求使用者確認正在操作的裝置。裝置應安全顯示碼,避免日誌、分析事件或截圖洩露;伺服器不應把存取權杖放進此回應。

3. 處理使用者互動與授權綁定

使用者開啟驗證 URI 後登入並看到客戶端名稱、要求範圍與裝置資訊。授權頁面必須讓使用者確認該裝置在自己身邊,防止攻擊者誘導使用者為另一台裝置授權。批准後只把工作階段狀態改為 AUTHORIZED,不直接把權杖透過瀏覽器回傳給裝置。拒絕操作寫成終態 DENIED,讓裝置在下一次輪詢得到明確錯誤。

4. 定義權杖端點狀態機

權杖端點以標準錯誤驅動裝置狀態:

text
poll(device_code):
  session = lookup(hash(device_code))
  if session is missing: return invalid_grant
  if now >= session.expiresAt: return expired_token
  if session.status == DENIED: return access_denied
  if session.status != AUTHORIZED: return authorization_pending
  atomically mark CONSUMED
  return issueAccessAndRefreshTokens(session)

同一 device code 的成功兌換必須原子化,避免兩個裝置執行緒取得兩組權杖。若裝置碼只允許一次兌換,重複請求回傳 invalid_grant 或協定約定的已消費錯誤;不要在失敗重試中重新建立授權工作階段。

5. 實施輪詢退避與伺服器保護

裝置首次輪詢不得早於回應中的 interval。收到 authorization_pending 時維持相同間隔;收到 slow_down 時至少增加 5 秒,再繼續輪詢。裝置端還要設定總截止時間、網路逾時與抖動,伺服器端按 device code、客戶端與 IP 限速。權杖端點應回傳禁止快取策略,避免代理重放狀態回應;高峰時寧可回傳可重試錯誤,也不能為每次輪詢建立昂貴背景工作。

6. 處理過期、取消與恢復

裝置必須把 expired_tokenaccess_denied 與永久 invalid_grant 視為終態,清理本地 device code,不再繼續輪詢。使用者可在裝置上選擇取消,伺服器將工作階段標記為 CANCELLED,後續請求不應恢復。網路中斷時保留截止時間並從同一 device code 恢復,不能自動申請多個工作階段導致使用者授權錯裝置。裝置重啟後若本地沒有安全保存工作階段狀態,應重新開始並通知使用者。

7. 處理 public client 與反釣魚安全

裝置端通常無法保護客戶端密鑰,應按 public client 對待;最小化存取範圍,更新權杖採輪換與撤銷策略。使用者碼要足夠短便於輸入,又要有足夠熵抵抗線上猜測;驗證端點對錯誤次數與 IP 組合限速。頁面顯示裝置名稱、型號或一次性短碼,並建議使用者確認裝置螢幕上的碼,降低遠端釣魚。日誌只記錄工作階段 ID、結果與雜湊後標識,不能記錄原始 device code 或權杖。

8. 可觀測性與測試

指標包括裝置授權建立率、授權完成率、平均完成時間、每個工作階段輪詢次數、slow_down 比例、過期與拒絕比例,以及權杖兌換衝突。測試涵蓋重複兌換、逾時、時鐘偏差、網路重試、惡意猜碼、並發輪詢、使用者拒絕、伺服器重啟與伺服器限流。端到端測試必須確認使用者批准的裝置與最終權杖工作階段一致,不能只驗證兩個 HTTP 端點分別回傳 200。

高品質示範回答

我會按 RFC 8628 建立一次性工作階段:裝置授權端點回傳高熵 device_code、短使用者碼、驗證 URI、過期時間與最小輪詢間隔,伺服器保存雜湊後裝置碼和明確狀態。使用者在手機瀏覽器登入後看到客戶端、範圍與裝置資訊並批准;權杖端點只接受 device code,authorization_pending 繼續輪詢,slow_down 增加至少 5 秒間隔,expired_tokenaccess_denied 與永久無效立即終止。成功兌換在交易中原子消費 device code,避免並發發放兩組權杖。裝置按 public client 處理,最小化範圍、輪換更新權杖、限制猜碼並要求使用者核對裝置螢幕上的短碼。指標與測試覆蓋全流程一致性、退避、恢復與釣魚邊界。

常見錯誤

  • 把 device code 當作使用者可輸入的短碼,導致日誌和 UI 暴露高價值憑證。
  • 忽略 slow_down,固定高頻輪詢權杖端點。
  • 把所有錯誤都當作繼續重試,過期或拒絕後仍然輪詢。
  • 用裝置端靜態客戶端密鑰假裝它是 confidential client。
  • 使用者批准後直接把權杖放入瀏覽器 URL 或頁面腳本。
  • 並發輪詢未原子消費 device code,發放多組更新權杖。
  • 只測試輪詢介面,不驗證使用者批准的裝置與最終權杖是否綁定。

追問及應對

如何選擇使用者碼長度與有效期?

在可輸入性、線上猜測成本與使用者完成時間之間權衡。使用字元集避免易混淆字元,伺服器限制每個碼的失敗次數與總嘗試速率;有效期要覆蓋使用者拿手機、登入與確認裝置的時間,但不能無限延長釣魚窗口。最終數值應由威脅模型與實際完成時間校準。

如何避免惡意客戶端佔滿授權工作階段?

按客戶端註冊、IP、裝置指紋與帳號維度限制建立速率,限制每個主體的未完成工作階段數,並讓過期工作階段非同步清理。建立介面先驗證客戶端與 scope,再產生工作階段;不能把垃圾 device code 的清理壓力轉移給權杖端點。

使用者批准後裝置離線,之後如何恢復?

工作階段保留到過期時間,裝置恢復網路後可繼續使用原 device code 輪詢。伺服器應回傳成功權杖或明確終態;裝置端為權杖兌換請求設定冪等處理,避免網路回應遺失後重複消費。超過有效期則重新開始授權並顯示新的短碼。

verification_uri_complete 有什麼風險?

它把使用者碼放入連結,減少輸入但增加連結洩露與遠端釣魚風險。頁面仍應顯示短碼並要求使用者確認裝置螢幕上的相同值;不要在分析、Referer、歷史記錄或第三方跳轉中暴露完整連結。可按裝置能力與威脅模型決定是否提供。

為什麼不讓手機把存取權杖直接傳給裝置?

跨裝置傳輸權杖會擴大瀏覽器、QR code、剪貼簿與中間頁面的暴露面,也難以保證權杖交給正確裝置。Device Flow 讓裝置用自己的 device code 在 TLS 連線中兌換權杖,使用者頁面只改變伺服器狀態;若產品需要近場綁定,應另加經認證的安全通道。

如何監控輪詢系統是否正確退避?

記錄工作階段級輪詢次數、時間間隔分布、slow_down 後的實際間隔、終態耗時與按客戶端聚合的失敗率。用受控測試注入重複輪詢,驗證伺服器回傳 slow_down 後客戶端至少增加 5 秒且不會產生同步風暴;把異常高頻客戶端隔離,不用全域放寬間隔。

公開來源

同類題目