OAuth 2.0 – 设备流(Device flow)为工程师(尤其是后端工程师)详解

发布: (2026年5月12日 GMT+8 00:11)
6 分钟阅读

Source: Stack Overflow Blog

第一次在酒店的电视上尝试登录 Netflix 时,我差点放弃。遥控器只有四个方向键和一个数字键盘,而我的密码长达 18 个字符并带有符号。几年后,同样的电视开始显示一个短码和一个 URL。我打开手机,输入该 URL,填写代码,便成功登录——无需遥控器的繁琐操作,也不需要在电视上输入密码。

这就是 OAuth 2.0 device authorization grant,通常称为 device flow。如果你曾运行过 aws sso logingh auth login,或在 Xbox 上登录 Spotify,你已经使用过它。构建 CLI、物联网设备、智能电视应用或任何输入受限的客户端的后端时,最终都需要实现它。

设备流程概述

  1. 客户端(例如 CLI)向授权服务器请求 device code(设备码)和 user code(用户码)。
  2. 服务器返回这些码以及验证 URI 和轮询参数。
  3. 客户端向用户显示用户码和验证 URI。
  4. 用户在另一台设备上打开该 URI,输入用户码,进行身份验证并批准请求。
  5. 与此同时,客户端轮询 token endpoint,直至用户完成步骤或出现错误。

如果客户端正确处理这五种可能的轮询响应,流程即完成。

步骤式实现

步骤 1 – 请求设备码

POST /oauth/device_authorization HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

client_id=mycli-prod&scope=read:repos%20write:repos

典型响应

{
  "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客户端保密的不可读字符串。
user_code向用户展示的短小可读代码。
verification_uri用户输入 user_code 的 URL。
expires_in设备码的有效期(通常为 15–30 分钟)。
interval最小轮询间隔(秒)。

步骤 2 – 向用户显示说明

在浏览器中打开此 URL:
https://example.com/device

输入以下代码:
  WDJB-MJHT

等待确认...

用户在手机或笔记本电脑上打开该 URL,输入代码,登录(例如通过 SSO),并批准请求。

步骤 3 – 轮询令牌端点

POST /oauth/token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS
&client_id=mycli-prod

可能的响应

响应含义操作
authorization_pending用户尚未批准。继续轮询。
slow_down轮询过快。将间隔增加 ≥ 5 秒。
expired_token设备码已过期。停止轮询,重新开始流程。
access_denied用户拒绝请求。停止轮询,通知用户。
成功 (200)已授予令牌。使用返回的令牌。

成功响应示例

{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "v1.MRn7...",
  "scope": "read:repos write:repos"
}

常见陷阱和最佳实践提示

  1. user_code 视为公开信息,而非机密
    它由用户在另一台设备上输入,因此应简短但不可猜测。典型格式为 8 – 9 位字母数字字符,且不含易混淆的符号(例如不使用 0OI1)。GitHub 使用 XXXX XXXX

  2. 对验证端点进行速率限制
    用户提交 user_code 的页面是暴力破解的目标。即使字母表已缩小,也可能同时存在数千个待处理的代码。请对每个 IP 或每个会话实施限制,并在失败后采用指数退避。

  3. 遵守 slow_down
    某些客户端会忽略此指令,继续以原始间隔轮询,这可能触发滥用检测。务必遵守服务器请求的间隔增加。

  4. 在使用后立即使 user_code 失效
    原子地将代码标记为已消费。“检查‑后‑标记”的竞争条件会导致代码被重复使用,从而产生安全漏洞。

  5. 不要把设备授权流程与 PKCE 混淆
    设备授权流程解决的是输入受限设备的问题。PKCE 适用于无法保守密钥的公共客户端(移动应用、SPA)。在适当的情况下,它们可以结合使用(设备授权 + PKCE)。

结束语

设备授权流程归结为 两个端点五种响应情况。一旦你实现了可用的实现,你会惊讶于为什么仍有人让用户在 CLI 提示符中粘贴个人访问令牌。实现一次,你的用户会感激不已。

0 浏览
Back to Blog

相关文章

阅读更多 »