上一篇 Day 12 談的是 Client Credentials Grant——完全不需要使用者參與的機器對機器(M2M)情境。今天要回到「有使用者」的場景,但使用者手上的裝置不太一樣:智慧電視、遊戲主機、IoT 裝置、CLI 工具⋯⋯這些裝置常常沒有瀏覽器,或是輸入能力很差。OAuth 2.0 針對這種情境定義了 Device Authorization Grant(RFC 8628),俗稱 Device Flow。
今天內容涵蓋
RFC 8628 的 Abstract 說得很明楚:這個 Grant 是為了「連上網路,但缺乏瀏覽器、或輸入能力受限到無法在授權流程中輸入文字」的裝置設計的,例如智慧電視、媒體主機、數位相框、印表機。它讓這些裝置上的 OAuth Client,可以透過使用者在「另一台裝置」上的使用者代理(瀏覽器)完成授權。
RFC 8628 §1 也列出了使用這個 Grant 的四個前提條件:
因此,Device Authorization Grant 主要用於缺乏瀏覽器、或文字輸入能力受限的裝置。若是智慧型手機這類可以正常開啟瀏覽器、接收重新導向的原生 App,通常仍應採用前面介紹過的 Authorization Code + PKCE,而不是改用 Device Flow。
常見的適用場景:
Device Authorization Grant 其實包含兩條並行的路徑:一條發生在「裝置本身」(Device Flow),另一條發生在使用者的「瀏覽器」(Browser Flow)。裝置會先向 Authorization Server 取得代碼,接著把驗證網址與使用者代碼顯示給使用者;使用者改用手機或電腦完成登入與授權,裝置端則持續輪詢 Token Endpoint,等待授權完成後取得 Access Token。

以上圖來看,流程可以拆成十個步驟:
client_id 與 scope,向 Authorization Server 的 Device Authorization Endpoint 發出請求。device_code、user_code、verification_uri,以及有效時間與輪詢間隔等資訊。verification_uri 與 user_code 顯示給使用者,或提供 QR Code,請使用者改用另一台裝置操作。verification_uri。user_code,用來對應原本那台等待授權的裝置。interval 指定的輪詢間隔,持續帶著 device_code 向 Token Endpoint 查詢授權是否完成。整段流程中,裝置與使用者瀏覽器沒有直接的雙向通訊。使用者在瀏覽器端完成「授權確認」;裝置端則透過輪詢 Token Endpoint 得知授權是否完成。這也是它和 Authorization Code Grant 最大的不同:Device Flow 不需要瀏覽器重新導向回原本的裝置。
從這一節開始,會把流程中的 HTTP 請求與回應拆開來看。
第一個階段對應流程圖的步驟 1、2。Device Client 會先向 Authorization Server 的 Device Authorization Endpoint 送出請求,要求建立一組本次授權流程使用的代碼:device_code 給裝置端後續輪詢使用,user_code 則顯示給使用者,讓使用者在另一台裝置上完成驗證。
POST /device_authorization HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
client_id=1406020730&scope=example_scope
| 參數 | 說明 |
|---|---|
client_id |
Device Client 的識別碼,用來讓 Authorization Server 知道是哪一個應用程式在發起 Device Flow。多數 Device Flow 情境下都會帶上這個參數。 |
scope |
選填。用來表示這次授權想取得哪些存取範圍,例如讀取使用者資料、呼叫特定 API 等。 |
成功時,Authorization Server 回傳 200 OK。這個回應的重點,是把「給裝置用」和「給使用者用」的資訊分開:
{
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
"user_code": "WDJB-MJHT",
"verification_uri": "https://example.com/device",
"verification_uri_complete": "https://example.com/device?user_code=WDJB-MJHT",
"expires_in": 1800,
"interval": 5
}
| 欄位 | 必填 | 說明 |
|---|---|---|
device_code |
必填 | 裝置端使用的代碼,後續會拿去 Token Endpoint 查詢授權結果,不應顯示給使用者 |
user_code |
必填 | 使用者輸入的代碼,用來把瀏覽器端的授權動作對應回原本那台裝置 |
verification_uri |
必填 | 使用者要開啟的驗證頁面網址,通常會設計得簡短好記 |
verification_uri_complete |
選填 | 已經把 user_code 放進網址的版本,適合做成 QR Code,讓使用者掃描後直接進入驗證頁面 |
expires_in |
必填 | device_code 與 user_code 的有效秒數,過期後需要重新發起流程 |
interval |
選填 | Authorization Server 建議的輪詢間隔;如果沒有回傳,Device Client 會使用預設的輪詢間隔 |
Device Client 拿到 device_code 之後,還不能直接取得 Access Token;它必須等使用者在另一台裝置完成登入與授權,再用 device_code 去 Token Endpoint 查詢結果。
取得 device_code 後,Device Client 接下來要做的事情,是定期向 Token Endpoint 查詢授權結果。
這也是 Device Flow 和 Authorization Code Grant 很大的差異。Authorization Code Grant 會透過瀏覽器的 redirect_uri 把結果導回 Client;但 Device Flow 面對的是 Smart TV、遊戲主機、CLI 或 IoT 裝置,因此改用「輪詢」的方式,主動向 Authorization Server 查詢:使用者完成授權了嗎?可以核發 Access Token 了嗎?
Device Client 輪詢 Token Endpoint 時,grant_type 固定使用 Device Code Grant 的識別值:
urn:ietf:params:oauth:grant-type:device_code
送到 Token Endpoint 的請求通常會包含以下參數:
| 參數 | 必填 | 說明 |
|---|---|---|
grant_type |
必填 | 表示這次使用 Device Code Grant,也就是 urn:ietf:params:oauth:grant-type:device_code |
device_code |
必填 | 第三節取得的裝置端代碼。Device Client 每次輪詢 Token Endpoint 時都會帶上它,讓 Authorization Server 知道這次查詢是對應到哪一台正在等待授權的裝置。 |
client_id |
必填 | Device Client 的識別碼,用來確認是哪個應用程式在查詢授權結果 |
輪詢時,Device Client 不能無限制地一直送請求。它應該依照回傳的 interval 等待指定秒數後再查詢;如果回應中沒有 interval,就使用預設的 5 秒作為輪詢間隔。例如 interval: 5 代表 Device Client 至少要等 5 秒,才能再次向 Token Endpoint 查詢授權狀態。
在使用者完成授權之前,Device Client 通常還拿不到 Access Token。這時 Token Endpoint 不會直接回傳成功結果,而是用錯誤碼告訴 Client 目前流程進行到哪裡:
| 錯誤碼 | 意思 |
|---|---|
authorization_pending |
使用者還沒完成授權,Client 應該依照輪詢間隔繼續等待並再次查詢 |
slow_down |
輪詢頻率太快,之後每次請求的等待時間都要再增加 |
access_denied |
使用者拒絕了這次授權請求,Client 應停止輪詢 |
expired_token |
device_code 已過期,Client 應停止輪詢,並讓使用者重新發起流程 |
只有 authorization_pending 和 slow_down 代表「流程尚未結束,可以繼續等待」。收到 access_denied 或 expired_token 時,Device Client 就不應再繼續輪詢,而是應該停止流程並把結果呈現給使用者。
等使用者在瀏覽器端完成登入與授權後,Authorization Server 會在下一次輪詢時透過 Token Endpoint 回傳 Access Token;視設定不同,也可能一併回傳 Refresh Token。
Device Authorization Grant 的安全設計,重點是避免使用者在輸入受限、缺乏完整瀏覽器的裝置上直接輸入帳號密碼。它會把登入與同意授權移到手機或電腦的瀏覽器中完成,原本的 Device Client 只負責顯示代碼、等待結果,並在授權完成後透過 Token Endpoint 取得 Access Token。
因此,Device Flow 不是保證「一定安全」,而是透過幾個設計降低風險:
| 安全設計 | 作用 |
|---|---|
| 不在受限裝置輸入帳密 | 避免使用者在 Smart TV、遊戲主機、CLI 或 IoT 裝置上直接輸入帳號密碼 |
| 使用另一台裝置完成登入 | 使用者改在手機或電腦的瀏覽器中登入,Authorization Server 仍可套用 MFA、Passkey、SSO 等既有安全機制 |
device_code 不能直接換 Token |
Device Client 拿到 device_code 後只能輪詢;必須等使用者完成登入並同意授權,Token Endpoint 才會回傳 Access Token |
| 代碼有有效期限 | device_code 與 user_code 都有 expires_in,可縮短代碼外洩後能被利用的時間 |
| 輪詢頻率受限制 | Device Client 必須遵守 interval;如果輪詢太快,Authorization Server 可以用 slow_down 要求放慢 |
| 授權結果由 Token Endpoint 回傳 | 裝置不需要接收瀏覽器重新導向,而是由裝置端主動輪詢 Token Endpoint 取得結果 |
不過,這些設計只能降低風險,不能消除所有風險。Device Flow 仍然需要注意以下問題:
| 風險 | 說明 | 防護方式 |
|---|---|---|
user_code 被暴力猜測 |
user_code 需要讓人手動輸入,通常不會設計得太長;如果沒有嘗試次數限制,就可能被猜中 |
提高亂數強度、限制錯誤嘗試次數、縮短有效期限 |
device_code 外洩 |
device_code 是裝置輪詢 Token Endpoint 時使用的代碼,不應顯示給使用者或暴露在不安全通道中 |
僅透過 HTTPS 傳輸、不顯示給使用者、縮短有效期限、限制單次使用 |
| 輪詢濫用 | Device Client 若不遵守輪詢間隔,可能造成 Token Endpoint 壓力,也可能被用來做大量嘗試 | 遵守 interval,收到 slow_down 時增加等待時間 |
| Device Code Phishing | 攻擊者可以先在自己的裝置上發起 Device Flow,再誘騙使用者到官方驗證頁輸入代碼,讓使用者誤授權攻擊者的裝置 | 驗證頁面清楚顯示裝置名稱、應用程式與要求的權限,並提醒使用者只輸入自己正在操作的裝置代碼 |
| 共用瀏覽器 Session | 使用者若在公用電腦上完成登入,可能留下已登入的 Session,讓下一位使用者接續操作 | 完成流程後提示關閉瀏覽器、避免在公用裝置登入、必要時要求重新驗證 |
所以,Device Authorization Grant 的安全性取決於 Authorization Server 與 Client 是否正確實作這些限制。比較好的實作會縮短代碼有效期限、限制錯誤嘗試次數、強制使用 HTTPS、限制代碼只能使用一次,並在驗證頁面清楚顯示正在授權的裝置與權限範圍。
Device Authorization Grant 的重點,是讓「不好輸入帳密、也不適合跑完整瀏覽器流程」的裝置,透過另一台裝置完成使用者授權。
這裡最容易混淆的是 user_code 和 device_code:
user_code 是給使用者看的代碼,會顯示在裝置畫面上,讓使用者到另一台裝置的瀏覽器輸入。device_code 是給 Device Client 使用的代碼,不應顯示給使用者;裝置會用它輪詢 Token Endpoint,確認授權是否完成。因此,Device Flow 並不是讓登入變得「絕對安全」,而是把帳密輸入移到比較適合的瀏覽器環境中處理,再透過短效代碼、輪詢間隔、錯誤限制與使用者確認,降低風險。
下一篇要看的是 JWT Bearer Grant。它和 Device Authorization Grant 不同,不是讓使用者換到另一台裝置完成授權,而是讓 Client 拿一份已簽章的 JWT Assertion,向 Authorization Server 換取 Access Token,常見於企業內部系統或服務對服務的授權場景。