设备码授权概览
本文说明什么是设备码授权。
如果你需要进一步了解如何用 CloudBase CLI 与参考实现完成具体对接,请继续阅读 企业自建设备码授权服务对接 CloudBase CLI。
1. 为什么要用设备码授权
设备码授权解决的核心问题是:发起登录请求的终端,与完成网页登录确认的浏览器,往往不在同一台机器,也不在同一个交互界面里。
典型场景包括:
- 用户在远程服务器、跳板机、容器或云端开发机里发起 CloudBase 登录,但浏览器在本地电脑上。
- 用户通过聊天工具或类 OpenClaw 的对话式 AI 工具触发 CloudBase 登录,请求是由机器人或远端执行器发起的,浏览器确认却发生在用户自己的设备上。
这类场景下,传统“终端直接拉起浏览器并等待回调”的登录方式往往不 可用。设备码授权把流程拆成两段:
- 终端先申请一组
device_code和user_code。 - 用户再去浏览器里确认“是否允许这次 CloudBase 登录”。
这样做的价值在于:
- 终端侧只负责发起请求和轮询结果,不要求能完成浏览器回调。
- 浏览器侧只负责身份认证和授权确认,不要求和使用 CLI/MCP 发起 CloudBase 登录的客户端在同一个进程或同一台机器上。
- 同一套流程既能支持普通终端,也能支持聊天工具、代理执行器、远程任务这类异步交互场景。
2. 协议约定
一套设备码授权服务通常需要实现 3 个核心接口:
| 接口 | 方法 | 作用 |
|---|---|---|
/auth/device/code | POST | 终端申请设备码,获取 device_code、user_code、verification_uri |
/auth/device/verify | POST | 浏览器侧确认授权,把设备码状态从 pending 更新为 authorized |
/auth/token | POST | 负责首次换取凭证、刷新凭证、撤销会话 |
其中 /auth/token 通过 grant_type 区分 3 种动作:
grant_type | 作用 |
|---|---|
urn:ietf:params:oauth:grant-type:device_code | 使用 device_code 首次换取凭证 |
refresh_token | 使用 refreshToken 刷新 临时凭证,并轮换新的 refreshToken |
revoke_token | 撤销 refreshToken 对应会话,供 tcb logout 使用 |
2.1 核心请求与响应
POST /auth/device/code
请求:
{
"client_id": "cloudbase-cli"
}
成功响应:
{
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS8A",
"user_code": "QWER-ASDF",
"verification_uri": "https://auth.example.com/cli-auth",
"expires_in": 600,
"interval": 3
}
POST /auth/device/verify
这是浏览器侧接口,路径可以由企业自行设计,但职责必须一致:
- 校验当前用户已经完成企业身份认证。
- 接收
user_code。 - 找到对应的
device_code记录。 - 把授权状态从
pending更新为authorized。 - 绑定当前用户身份,供后续签发临时凭证使用。
参考请求:
{
"user_code": "QWER-ASDF"
}
POST /auth/token with grant_type=device_code
终端轮询请求:
{
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS8A",
"client_id": "cloudbase-cli",
"device_info": {
"os": "darwin",
"mac": "AA:BB:CC:DD:EE:FF",
"hash": "a1b2c3d4e5f6..."
}
}
成功响应需要返回一组可供客户端保存和后续使用的登录凭证,核心字段如下:
| 字段 | 说明 |
|---|---|
refreshToken | 长期令牌;如果实现自动续期,必须返回非空值 |
expired | refreshToken 的过期时间戳(毫秒) |
tmpSecretId | 腾讯云临时密钥 SecretId |
tmpSecretKey | 腾讯云临时密钥 SecretKey |
tmpToken | 腾讯云临时安全令牌 |
tmpExpired | 临时密钥过期时间戳(毫秒) |
uin | 当前登录用户标识 |
tokenId | 会话或令牌 ID |
envIds | 可选 |
envList | 可选 |
envBillingInfoList | 可选 |
如果你的企业自建授权服务需要在自定义 endpoint 场景下返回这 3 个字段,可以按下面的结构组织:
envIds:
[
"cloud1-123456",
"cloud1-abcdef"
]
envList:
| 字段 | 类型 | 说明 |
|---|---|---|
EnvId | string | 环境 ID |
Alias | string | 环境别名 |
Status | string | 环境状态 |
Source | string | 环境来源,例如小程序或腾讯云 |
CreateTime | string | 创建时间 |
UpdateTime | string | 最后更新时间 |
PackageId | string | 套餐 ID |
PackageName | string | 套餐名称 |
PayMode | string | 支付方式 |
IsDefault | boolean | 是否默认环境 |
Region | string | 环境地域 |
envBillingInfoList:
| 字段 | 类型 | 说明 |
|---|---|---|
EnvId | string | 环境 ID |
PackageId | string | 套餐 ID |
IsAutoRenew | boolean | 是否自动续费 |
Status | string | 计费状态 |
PayMode | string | 计费模式 |
IsolatedTime | string | 隔离时间 |
ExpireTime | string | 过期时间 |
CreateTime | string | 首次接入计费时间 |
UpdateTime | string | 最近更新时间 |
IsAlwaysFree | boolean | 是否从未升级过付费版 |
PaymentChannel | string | 支付渠道 |
FreeQuota | string | 免费额度信息 |
EnableOverrun | boolean | 是否开启超额按量付费 |
ExtPackageType | string | 环境套餐类型 |
如果你需要进一步实现这两个对象数组,可参考调用腾讯云云 API 获取对应返回结构:
DescribeEnvs:https://cloud.tencent.com/document/product/876/34820DescribeBillingInfo:https://cloud.tencent.com/document/product/876/94390
POST /auth/token with grant_type=refresh_token
{
"grant_type": "refresh_token",
"refresh_token": "<refreshToken>",
"client_id": "cloudbase-cli"
}
推荐行为:
- 校验当前 refresh token。
- 重新签发临时凭证。
- 轮换新的
refreshToken。 - 让旧
refreshToken立即失效。
POST /auth/token with grant_type=revoke_token
{
"grant_type": "revoke_token",
"refresh_token": "<refreshToken>",
"client_id": "cloudbase-cli"
}
推荐行为:删除或失效对应会话,并返回 {}。这个接口应保持幂等。
2.2 建议错误码
| 错误码 | 典型场景 |
|---|---|
authorization_pending | 用户还没有在浏览器中确认授权 |
slow_down | 客户端轮询过快 |
expired_token | device_code 已过期 |
invalid_client | client_id 缺失、非法或不匹配 |
invalid_grant | device_code、refresh_token 或授权状态异常 |
unsupported_grant_type | 不支持的 grant_type |
already_consumed | 设备码已成功使用过 |
server_error | 服务端签发临时凭证失败 |