OAuth Device Flow 简明指南:CLI 如何通过浏览器完成授权
---
title: OAuth Device Flow 简明指南:CLI 如何通过浏览器完成授权
published_at: 2026-07-10
language: zh
---
# OAuth Device Flow 简明指南:CLI 如何通过浏览器完成授权
随着 AI Agent 和开发者工具的发展,CLI 正重新成为常见的交互与集成入口。GitHub CLI `gh`、Lark/Feishu `lark-cli`、Codex CLI 等工具在调用云端 API 时,都需要完成用户授权,而 OAuth Device Flow 正是这类场景中常见的一种方式。
OAuth Device Flow,也叫 Device Authorization Flow,主要用于那些不方便打开浏览器、不适合输入密码,或者无法接收 redirect URI 回调的设备和工具,例如智能电视、游戏机、IoT 设备、服务器终端和 CLI 工具。
它的核心思路可以概括为:
> 设备不直接让用户输入密码,而是显示一个短码;用户用手机或电脑打开浏览器完成登录授权;设备在后台轮询,直到拿到 access token。
## 它解决什么问题
普通 OAuth Authorization Code Flow 通常依赖浏览器跳转和 redirect URI。桌面应用可以在本机启动一个回调服务,Web 应用可以接收服务器回调,但很多设备做不到:
- 电视、游戏机输入账号密码很麻烦;
- CLI 工具可能运行在没有浏览器的 SSH 环境里;
- IoT 设备通常没有完整浏览器,也没有稳定公网回调地址;
- 用户密码和 MFA 不应该输入到不可信或输入体验差的设备上。
Device Flow 的作用,就是把登录和授权动作转移到用户更方便操作的手机或电脑上完成。
## 参与角色
- **Device Client**:受限设备或 CLI 工具,负责显示验证码并轮询 token。
- **Authorization Server**:OAuth 授权服务器,负责生成验证码、处理登录授权、发放 token。
- **User**:用户本人,用手机或电脑完成授权。
- **Resource Server**:真正提供 API 的服务,例如 GitHub API、Google Drive API。
## 完整流程
### 1. 设备申请 device code
设备先向授权服务器请求一组用于设备授权的码:
```http
POST /oauth/device/code
Content-Type: application/x-www-form-urlencoded
client_id=abc123&scope=repo read:user
```
这里通常包含:
- `client_id`:客户端标识;
- `scope`:希望申请的权限范围。
Device Flow 的客户端通常是 public client,不能安全保存 `client_secret`。
### 2. 授权服务器返回验证码信息
授权服务器返回类似结果:
```json
{
"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": 900,
"interval": 5
}
```
字段含义:
| 字段 | 作用 |
|---|---|
| `device_code` | 给设备使用的长码,后续轮询 token 时提交 |
| `user_code` | 给用户看的短码,用户在网页上输入 |
| `verification_uri` | 用户需要打开的授权页面 |
| `verification_uri_complete` | 已带上 user code 的完整授权链接,用户打开后可能不必手动输入 |
| `expires_in` | 这组码的有效期,单位通常是秒 |
| `interval` | 设备轮询 token 接口的最小间隔 |
### 3. 设备提示用户授权
设备或 CLI 显示:
```text
请打开下面的链接完成授权:
https://example.com/device
然后输入验证码:
WDJB-MJHT
```
如果有 `verification_uri_complete`,也可以直接显示完整链接:
```text
https://example.com/device?user_code=WDJB-MJHT
```
### 4. 用户在浏览器里登录并确认
用户用手机或电脑打开授权页面,输入验证码,登录账号,并确认是否允许这个应用访问指定权限。
授权服务器会更新这次设备授权请求的状态:已授权、被拒绝,或者已过期。
### 5. 设备轮询 token 接口
设备从拿到 `device_code` 开始,按 `interval` 指定的间隔请求 token 接口:
```http
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
client_id=abc123&device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS&grant_type=urn:ietf:params:oauth:grant-type:device_code
```
关键参数是:
```text
grant_type=urn:ietf:params:oauth:grant-type:device_code
```
它表示客户端正在用 Device Flow 的授权结果换取 token。
### 6. 授权成功后返回 access token
用户完成授权后,设备下一次轮询会收到:
```json
{
"access_token": "access_token_here",
"token_type": "Bearer",
"scope": "repo read:user",
"expires_in": 3600,
"refresh_token": "refresh_token_here"
}
```
之后设备就可以带着 access token 调用资源服务器:
```http
GET /user
Authorization: Bearer access_token_here
```
## 流程图
```text
设备 / CLI 授权服务器 用户浏览器
| | |
| 申请 device_code | |
|----------------------------->| |
| 返回 device_code/user_code | |
|<-----------------------------| |
| 显示 URL 和 user_code | |
| | 用户打开页面、输入 code、登录 |
| |<----------------------------->|
| 轮询 token 接口 | |
|----------------------------->| |
| authorization_pending | |
|<-----------------------------| |
| 继续轮询 | |
|----------------------------->| |
| 返回 access_token | |
|<-----------------------------| |
```
## 轮询时的几种状态
设备轮询 token 接口时,授权不一定已经完成。常见返回状态如下:
| 错误码 | 含义 | 客户端应该怎么做 |
|---|---|---|
| `authorization_pending` | 用户还没有完成授权 | 继续等待,按 `interval` 轮询 |
| `slow_down` | 轮询太频繁 | 增加轮询间隔 |
| `access_denied` | 用户拒绝授权 | 停止流程,提示授权被拒绝 |
| `expired_token` | device code 已过期 | 停止流程,让用户重新发起登录 |
| `invalid_client` | client_id 不合法 | 检查客户端配置 |
| `invalid_grant` | device_code 无效或状态异常 | 停止流程,重新开始 |
客户端不应该把 `authorization_pending` 当成失败。它只是表示用户还没完成网页端授权。
## 为什么 Device Flow 适合 CLI、电视和 IoT 设备
Device Flow 的优势在于:
- 设备不需要打开浏览器;
- 设备不需要接收 redirect URI 回调;
- 用户不用在设备上输入密码和 MFA;
- 登录和授权发生在授权服务器的正规网页上;
- CLI 或设备只需要显示短码,并在后台轮询。
所以它尤其适合输入能力弱、没有浏览器、没有公网回调地址的场景。
## 安全要点
Device Flow 的安全性依赖几个关键设计:
1. **用户密码不进入设备**
用户只在授权服务器网页上登录,设备只拿到 token。
2. **user code 短但有效期也短**
短码方便输入,但也更容易被猜,所以必须设置过期时间、错误次数限制和风控。
3. **轮询必须限速**
客户端要遵守 `interval`,遇到 `slow_down` 要放慢请求,避免压垮授权服务器。
4. **授权页面必须展示应用和权限**
用户需要清楚看到哪个应用在请求哪些权限,避免误授权。
5. **适合 public client**
电视、CLI、IoT 设备通常不能安全保存密钥,因此 Device Flow 不依赖客户端安全保存 `client_secret`。
## 小结
Device Flow 可以压缩成 6 步:
1. 设备向授权服务器申请 `device_code`;
2. 授权服务器返回 `device_code`、`user_code` 和授权 URL;
3. 设备显示 URL 和 `user_code`;
4. 用户在另一台设备上打开网页,登录并授权;
5. 设备按间隔轮询 token 接口;
6. 授权完成后,设备拿到 access token 并调用 API。
一句话总结:
> Device Flow 是 OAuth 为受限设备设计的授权方式:设备显示短码,用户在浏览器里授权,设备轮询直到拿到 token。