跳到正文
山月行
返回

设备码登录:没有浏览器时,CLI 和 Agent 怎么拿到令牌

发布于

2025 年之后,不少开发平台的命令行改成了设备码登录。vercel login 是我最近常碰到的一个:终端打出一串短码和一个网址,人换到手机或另一台电脑上确认,令牌再回到原来那台机器。

这套流程的正式名字是 OAuth 2.0 Device Authorization Grant,文档是 RFC 8628,2019 年 8 月发布。规范本身不新。新的是使用现场:SSH 上的服务器、CI、容器、远程 IDE,还有 Grok Bot 这类 Agent 沙箱,常常没有一块能把回调打回本机的浏览器。

RFC 8628 本来写给智能电视、媒体盒子、电子相框和打印机。它们连着网,能发出 HTTPS 请求,也能把网址和短码显示给人,但没有合适的浏览器,也没法让人舒服地输入密码。CLI 和 Agent 沙箱是同一形状。

一、一次登录实际在交换什么

设备码把两件事拆开:哪台设备将拿到令牌,以及人在哪台设备上点同意。授权服务器在中间发两份码,一份只留给设备轮询,一份只给人看。

设备码登录总览:终端向授权端点要码,留下 device_code,把 user_code 和验证网址给人,人在另一台设备的浏览器里确认,设备再轮询换到 access_token

图里灰色是发起登录的终端,紫色是要保管的码,橙色是人用的浏览器,绿色是最后拿到的令牌。后面几张图沿用这套颜色:灰是起点,紫是码和令牌,橙是浏览器或平台侧,绿是登录完成,红是核对、拒绝或停用。

要用设备码,四条得同时成立:

  • 设备已经连上网。
  • 设备能主动发出 HTTPS 请求。
  • 设备能把网址和短码显示给用户。
  • 用户另有一台电脑或手机,用来完成确认。

规范把设备要打的地址叫 device authorization endpoint,和浏览器访问的授权端点不是同一个。设备用表单 POST 上去。公开客户端带 client_id,需要权限范围时再带 scope。

人的动作很短。打开验证网址,必要时输入短码,在已经登录的会话里点同意或拒绝。点同意之前,设备反复拿到的答复是「还没批」。

轮询打的仍是原来的 token endpoint。设备不新开一个发令牌的地址。

二、响应里每份字段各走各的

下面是 RFC 8628 里的示例,不是某个厂商的真实响应。device_code 那一长串不应该出现在给人看的界面上。

{
  "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 和 expires_in。interval 可选,没给就按 5 秒。verification_uri_complete 也可选,里面已经带上 user_code。

expires_in 同时管这两份码,单位是秒。示例里的 1800 是 30 分钟,各家服务会换成自己的数字。过期后这次登录作废,要重新要一组码。

两份码分开走:授权端点一次返回 device_code 和 user_code,device_code 留在设备上轮询,user_code 只配验证网址给人,人在页面上核对短码

device_code 是轮询凭证。熵要高,因为猜中它的人,有机会在用户批完之后领走这次授权。规范建议交互过程里不要展示它,免得和短码搞混。

user_code 必须给人看,而且要短到能念、能手打。短就意味着熵低,授权服务器应对输错次数做限速。限速和有效期一起,把暴力尝试压到不划算。

verification_uri 保持短、好记。规范不建议把 user_code 拼进这个网址,否则手打的字符串变长,打错了也不容易当场发现。带码的地址交给 verification_uri_complete,给二维码或 NFC 用。

即便展示了二维码,规范仍要求把 user_code 用文字再显示一遍。授权页也应当把短码秀出来,让人核对它和设备屏幕上是不是同一串。核对的是「我批的是眼前这台设备」。

换令牌时,设备向 token endpoint 发送固定的 grant_type:urn:ietf:params:oauth:grant-type:device_code,再加上 device_code。公开客户端还要带 client_id。

三、本地回调为什么在无头环境里断掉

另一条老路是授权码加本地回调。CLI 在本机听一个 localhost 端口,浏览器授权完跳回 http://127.0.0.1 上的这个端口,进程接过 code 再去换令牌。

笔记本上这条路很顺。命令打开系统浏览器,你点一下,窗口自己回来。

本地回调和设备码:本机要能打开浏览器并把回调打回 localhost,无头环境这条路会断;设备码改由第二台设备确认,令牌仍回到发起登录的 CLI

它默认两件事同时成立:这台机器能打开浏览器,授权完浏览器能访问这个端口。SSH、Docker、CI,以及没有图形界面的云主机,两条经常一起缺。浏览器在笔记本上,端口在服务器上。

SSH 隧道可以把端口转回来。能工作,但每个工具都要单独教,Agent 也不会自己去配隧道。

设备码不要求回调打回设备。第二台设备只要能打开 verification_uri,手机、公司电脑或旁边另一块屏幕都行。令牌只发给握着 device_code 的那一方。确认发生在手机上,登录态落在原来的终端里。

GitHub CLI 的 gh auth login 也提供同一类选项:终端打出一次性码,浏览器里确认。我没有写它的上线日,只把它当作已经长期存在的同类做法。

笔记本上浏览器就在手边时,本地回调少一次换设备,仍然省事。RFC 8628 写明,它不打算替换手机和桌面这类本来就有浏览器的原生应用,那些应走 RFC 8252 的原生应用实践。

四、为什么 2025 年之后特别常见

远程开发、一次性容器、云端编码 Agent,都是有 shell、没有你已经登录的浏览器。Agent 要替人部署、查日志、开预览,就得登录 Vercel 这类平台。

密码应留在浏览器里,沙箱只接收令牌。外网也打不进沙箱里临时开的回调端口。

无头服务器和 Agent:沙箱里的终端打印 user_code 和验证网址,人在手机浏览器里批准,access_token 回到沙箱,密码不进设备

终端出码,人在自己的浏览器里登录,核对这次请求从哪来,再让那台机器拿到当时 scope 范围内的 access_token。Grok Bot 和任何跑在服务器里的 Agent 都是这个形状:出站 HTTPS 有,入站回调没有,旁边有一台你信得过的手机。

Vercel 把设备码收成了 CLI 的主路径。更新说明 New Vercel CLI login flow 的发布日是 2025-09-12。vercel login 改为 OAuth 2.0 Device Flow,可以从任意能开浏览器的设备授权。

授权页会提醒你核对地点、IP 和请求时间。命令说明在 vercel login。

Vercel CLI 登录时间线:2025-09-12 起 vercel login 改用设备流,2026-02-26 起邮箱、Git 标志和 oob 等旧方式停用,team 的 SAML 保留到 2026-06-01

橙色是平台把登录改到设备流的那一天,红色是多数旧方式停止支持,灰色是更晚的 SAML 到期日。

同一则说明废弃了这些旧入口:vercel login email、--github、--gitlab、--bitbucket、--oob,以及 team(SAML)。2026-02-26 起,上面多数方式不再支持。team(SAML)留到 2026-06-01。

--oob 以前会把人带到 vercel.com/device。它和设备码是近亲,但不是现在这条 RFC 8628 流程,现已 obsolete。现在的 vercel login 就是设备流本身。

我把日期限制在这三处,因为它们写在 Vercel 的 changelog 里,能对上。别的云厂商 CLI 也常见设备码。没核到具体上线日之前,不把它们排进同一条时间线。

五、轮询时那几个错误码

用户还没点下去时,设备按 interval 重复请求 token endpoint。这是协议的正常部分。authorization_pending 表示码还有效、人还没做完,等满间隔再问。

轮询 token 端点:authorization_pending 表示继续等,slow_down 要求把间隔加 5 秒,用户同意后才返回 access_token

slow_down 仍表示还没批,但服务器嫌你问太勤。规范要求从这次起把轮询间隔增加 5 秒,后面每一次都沿用加长后的间隔。

连接超时另算。客户端必须自己降低频率。规范建议指数退避,例如每次超时把间隔翻倍。

access_denied 表示人拒绝了,或服务器不批。expired_token 表示 device_code 过期,这次会话结束。过期后可以再要一组新码。规范建议等用户有动作再开始,避免无人值守时空转。

除了 authorization_pending 和 slow_down,别的错误码都要停止轮询,并把原因显示出来。

人点了同意之后,token endpoint 按普通成功响应返回,其中有 access_token。令牌下来的那一刻,登录才算完成。

规范原文的流程图把刷新令牌画成可选。带不带 refresh_token、有效期多长,由授权服务器决定,设备码这一步没有另外规定。

轮询可以收成几行,间隔从响应里的 interval 起算,缺省是 5 秒:

wait interval
POST token endpoint
authorization_pending -> 再等
slow_down             -> interval += 5,再等
access_denied         -> 停,告诉用户被拒绝
expired_token         -> 停,等用户愿意时再要新码
其它错误              -> 停,并把错误显示出来
成功                  -> 保存 access_token

六、优势,以及批准前要看的那一眼

优势来自约束。设备侧只需要出站 HTTPS,不用监听端口,也不把密码或会话 cookie 放进终端。密码留在你已经信任的浏览器里。

Agent 沙箱拿到的是一次授权产生的令牌,范围是当时的 scope。人可以在第二台设备上做完注册、二次验证和公司 SSO。这些步骤本来就在浏览器里,不必搬进终端。

地点、IP、请求时间也只能放在浏览器会话里核对。终端里的一行短码给不了这些信号。Vercel 把这三样写进了授权页,用的就是浏览器里已有的会话信息。

代价同样明确。短码要给人看,就可能被别人拿去用。攻击者在自己的机器上发起设备流,再诱使你打开验证页、输入他的 user_code。规范把这叫远程钓鱼。

缓解办法是授权页写明你正在授权一台设备,并给出足够的设备信息,让人能发现这不是自己眼前那一台。只扫二维码、不看页面的话,这一步会被跳过。

批准前的核对:验证页展示 user_code,人核对地点、IP 和请求时间,对不上就拒绝,对得上设备才拿到令牌

设备码适合一种分工:待授权的机器没有你的浏览器,批准用的浏览器是你自己的。验证页如果是别人引导你打开的仿冒站,它救不了你。

看到短码,先核对网址是不是官方的 verification_uri,再核对页面上的地点、IP、时间是不是你这次终端请求,然后再点同意。

user_code 给人,device_code 留在设备上。二维码只用 verification_uri_complete。旁边仍应留下可手打的网址和短码,给扫不了码的人一条退路。

七、什么时候该用它

什么时候用设备码:这台机器开得开你的浏览器就用本地回调,SSH、CI 和 Agent 沙箱走设备码

笔记本上浏览器就在手边,本地回调少一次换设备,仍然省事。无头服务器、CI、远程容器、Grok Bot 这类沙箱,设备码是默认选项。

2025 年之后它变常见,是因为这种机器变多了,平台把登录主路径改到了已经存在的标准上。Vercel 从 2025-09-12 改 vercel login,并给旧方式排了 2026-02-26 和 2026-06-01 两个停用日。这是已经写明、可以核对的一个例子。


分享本文:

上一篇
Claude Code skill:四条规则与两个模式