題幹與適用場景
這是 OAuth 協定實作與安全設計題。RFC 8628 面向智慧電視、媒體主機、印表機等無法方便輸入或執行瀏覽器的裝置:裝置向授權伺服器申請 devicecode 與 usercode,使用者在另一台裝置的瀏覽器中完成登入與授權,原裝置再輪詢權杖端點。裝置通常是 public client,無法把長期客戶端密鑰藏在韌體。題目不要求用 Device Flow 取代能執行系統瀏覽器的原生應用程式授權碼流程。
面試官考察點
- 能否正確區分裝置碼、使用者碼、驗證 URI、存取權杖與更新權杖的生命週期。
- 能否把
authorizationpending、slowdown、expiredtoken、accessdenied映射到狀態機。 - 能否設計有界輪詢、退避、冪等與並發控制,避免權杖端點被打爆。
- 能否識別使用者碼釣魚、裝置冒認、暴力猜碼與 public client 風險。
回答前需要釐清的問題
先確認裝置是否能顯示 URI 與使用者碼、是否能發起 HTTPS、是否有可靠時鐘,以及授權伺服器是否支援 OIDC 登入。再確認裝置需要存取哪些資源、權杖是否必須綁定裝置、使用者碼有效期與輪詢間隔由誰配置、是否允許取消。若裝置已有系統瀏覽器,應優先考慮 RFC 8252 的授權碼流程,而非強行使用 Device Flow。
30 秒回答框架
我會實作兩個端點:裝置授權端點回傳短生命週期 devicecode、可手動輸入的 usercode、verificationuri、過期時間與最小輪詢間隔;權杖端點只接受 device code 並回傳權杖或標準錯誤。伺服器保存雜湊後的裝置碼、狀態、客戶端與過期時間。裝置依伺服器提供的 interval 輪詢,收到 authorizationpending 繼續,slow_down 增加間隔,成功後停止;過期、拒絕或無效立即終止。使用者頁面顯示正在授權的裝置資訊,降低釣魚風險。
分步驟深入解答
1. 建模參與者與一次授權工作階段
參與者包括受限裝置、授權伺服器、使用者瀏覽器與資源伺服器。裝置授權端點建立一次工作階段,產生高熵隨機 devicecode 與便於輸入的 usercode,關聯客戶端、要求範圍、建立時間、過期時間與輪詢狀態。資料庫只保存 device code 的不可逆摘要,避免讀庫洩露即可直接換取權杖;使用者碼需要獨立索引與嚴格嘗試次數。
2. 設計裝置授權回應
回應至少包含 devicecode、usercode、verificationuri、expiresin 與 interval。verificationuricomplete 可提供帶使用者碼的便利連結,但更應在裝置螢幕與使用者頁面同時顯示相同短碼,要求使用者確認正在操作的裝置。裝置應安全顯示碼,避免日誌、分析事件或截圖洩露;伺服器不應把存取權杖放進此回應。
3. 處理使用者互動與授權綁定
使用者開啟驗證 URI 後登入並看到客戶端名稱、要求範圍與裝置資訊。授權頁面必須讓使用者確認該裝置在自己身邊,防止攻擊者誘導使用者為另一台裝置授權。批准後只把工作階段狀態改為 AUTHORIZED,不直接把權杖透過瀏覽器回傳給裝置。拒絕操作寫成終態 DENIED,讓裝置在下一次輪詢得到明確錯誤。
4. 定義權杖端點狀態機
權杖端點以標準錯誤驅動裝置狀態:
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。收到 authorizationpending 時維持相同間隔;收到 slowdown 時至少增加 5 秒,再繼續輪詢。裝置端還要設定總截止時間、網路逾時與抖動,伺服器端按 device code、客戶端與 IP 限速。權杖端點應回傳禁止快取策略,避免代理重放狀態回應;高峰時寧可回傳可重試錯誤,也不能為每次輪詢建立昂貴背景工作。
6. 處理過期、取消與恢復
裝置必須把 expiredtoken、accessdenied 與永久 invalid_grant 視為終態,清理本地 device code,不再繼續輪詢。使用者可在裝置上選擇取消,伺服器將工作階段標記為 CANCELLED,後續請求不應恢復。網路中斷時保留截止時間並從同一 device code 恢復,不能自動申請多個工作階段導致使用者授權錯裝置。裝置重啟後若本地沒有安全保存工作階段狀態,應重新開始並通知使用者。
7. 處理 public client 與反釣魚安全
裝置端通常無法保護客戶端密鑰,應按 public client 對待;最小化存取範圍,更新權杖採輪換與撤銷策略。使用者碼要足夠短便於輸入,又要有足夠熵抵抗線上猜測;驗證端點對錯誤次數與 IP 組合限速。頁面顯示裝置名稱、型號或一次性短碼,並建議使用者確認裝置螢幕上的碼,降低遠端釣魚。日誌只記錄工作階段 ID、結果與雜湊後標識,不能記錄原始 device code 或權杖。
8. 可觀測性與測試
指標包括裝置授權建立率、授權完成率、平均完成時間、每個工作階段輪詢次數、slow_down 比例、過期與拒絕比例,以及權杖兌換衝突。測試涵蓋重複兌換、逾時、時鐘偏差、網路重試、惡意猜碼、並發輪詢、使用者拒絕、伺服器重啟與伺服器限流。端到端測試必須確認使用者批准的裝置與最終權杖工作階段一致,不能只驗證兩個 HTTP 端點分別回傳 200。
高品質示範回答
我會按 RFC 8628 建立一次性工作階段:裝置授權端點回傳高熵 devicecode、短使用者碼、驗證 URI、過期時間與最小輪詢間隔,伺服器保存雜湊後裝置碼和明確狀態。使用者在手機瀏覽器登入後看到客戶端、範圍與裝置資訊並批准;權杖端點只接受 device code,authorizationpending 繼續輪詢,slowdown 增加至少 5 秒間隔,expiredtoken、access_denied 與永久無效立即終止。成功兌換在交易中原子消費 device code,避免並發發放兩組權杖。裝置按 public client 處理,最小化範圍、輪換更新權杖、限制猜碼並要求使用者核對裝置螢幕上的短碼。指標與測試覆蓋全流程一致性、退避、恢復與釣魚邊界。
常見錯誤
- 把 device code 當作使用者可輸入的短碼,導致日誌和 UI 暴露高價值憑證。
- 忽略
slow_down,固定高頻輪詢權杖端點。 - 把所有錯誤都當作繼續重試,過期或拒絕後仍然輪詢。
- 用裝置端靜態客戶端密鑰假裝它是 confidential client。
- 使用者批准後直接把權杖放入瀏覽器 URL 或頁面腳本。
- 並發輪詢未原子消費 device code,發放多組更新權杖。
- 只測試輪詢介面,不驗證使用者批准的裝置與最終權杖是否綁定。
追問及應對
如何選擇使用者碼長度與有效期?
在可輸入性、線上猜測成本與使用者完成時間之間權衡。使用字元集避免易混淆字元,伺服器限制每個碼的失敗次數與總嘗試速率;有效期要覆蓋使用者拿手機、登入與確認裝置的時間,但不能無限延長釣魚窗口。最終數值應由威脅模型與實際完成時間校準。
如何避免惡意客戶端佔滿授權工作階段?
按客戶端註冊、IP、裝置指紋與帳號維度限制建立速率,限制每個主體的未完成工作階段數,並讓過期工作階段非同步清理。建立介面先驗證客戶端與 scope,再產生工作階段;不能把垃圾 device code 的清理壓力轉移給權杖端點。
使用者批准後裝置離線,之後如何恢復?
工作階段保留到過期時間,裝置恢復網路後可繼續使用原 device code 輪詢。伺服器應回傳成功權杖或明確終態;裝置端為權杖兌換請求設定冪等處理,避免網路回應遺失後重複消費。超過有效期則重新開始授權並顯示新的短碼。
verificationuricomplete 有什麼風險?
它把使用者碼放入連結,減少輸入但增加連結洩露與遠端釣魚風險。頁面仍應顯示短碼並要求使用者確認裝置螢幕上的相同值;不要在分析、Referer、歷史記錄或第三方跳轉中暴露完整連結。可按裝置能力與威脅模型決定是否提供。
為什麼不讓手機把存取權杖直接傳給裝置?
跨裝置傳輸權杖會擴大瀏覽器、QR code、剪貼簿與中間頁面的暴露面,也難以保證權杖交給正確裝置。Device Flow 讓裝置用自己的 device code 在 TLS 連線中兌換權杖,使用者頁面只改變伺服器狀態;若產品需要近場綁定,應另加經認證的安全通道。
如何監控輪詢系統是否正確退避?
記錄工作階段級輪詢次數、時間間隔分布、slowdown 後的實際間隔、終態耗時與按客戶端聚合的失敗率。用受控測試注入重複輪詢,驗證伺服器回傳 slowdown 後客戶端至少增加 5 秒且不會產生同步風暴;把異常高頻客戶端隔離,不用全域放寬間隔。