郭立 (leeguoo)

# 让 agent 自己点完「Sign in with Google」——chrome-use 啃下 OAuth 弹窗登录

AI agent 想自己登录账号,账号密码那条用 bitwarden-use 通了,OAuth 这条卡了大半天。讲清楚「Sign in with Google」背后三个窗口怎么传话、自动化为什么一碰就断、chrome-use 砍的三刀,以及最后全自动登进 x 的过程。

2026年6月30日 · 文章 · 公开

本页目录

chrome-use 把 id_token 从 Google popup 经 gsi/transform 回传给 opener,并保住 opener 不被销毁

我想让 hermes(我那个跑在另一台机器上的 agent)自己登录账号。不是我先登好、把 cookie 喂给它,是它从头自己登。

登录无非两种。一种是账号密码:agent 从我的 Bitwarden 金库取出用户名、密码、2FA 验证码,填进去就完事——这条我用 bitwarden-use 打通了,零人工把 github 登了进去,连 passkey 都能(私钥从金库取出来本地签 WebAuthn,不用掏手机碰指纹)。另一种是「Sign in with Google」这类第三方 OAuth。这条卡了我大半天。

下面讲清楚它为什么难,以及 chrome-use 最后怎么过的。

一次点击,底下三个窗口在传话

你在 x 的登录页点「使用 Google 账号登录」,看着就一下,其实底下是这样:

  1. 那个 Google 按钮不是 x 自己画的,是 accounts.google.com 的一个跨域 iframe 塞进来的。
  2. 点下去,弹出一个 popup 窗口,在里面选账号。
  3. 选完,Google 不把 token 写进 URL,而是把这个 popup 重定向到一个中转页 accounts.google.com/gsi/transform
  4. gsi/transform 只干一件事:通过 window.opener 把 id_token postMessage 回最初那个登录页(opener),然后自己关掉。
  5. opener 收到 token,x 才算登录成功,跳到 /home

关键在第 4 步:token 是 popup 隔着窗口喊回 opener 的,靠的是 window.opener 这层关系。一旦这层关系断了,token 就喊给了空气,popup 傻站在 gsi/transform 上不关,x 那边永远等不到,几秒后弹回登录选择页。

自动化为什么一碰就断

我用 chrome-use 驱动我真实的 Chrome(它通过一个浏览器扩展接管标签,叫 relay 模式)。问题出在三处。

第一,opener 被自己人误杀。 relay 给每个标签的调试会话用 tabId 当钥匙。x 登录页跳一次(它是跨进程导航),Chrome 给这个标签换了新 tabId,旧钥匙就失效了。更要命的是当时的"恢复"逻辑:一遇到失效会话,它直接把这个标签销毁、另开一个空白标签顶上——而被销毁的,正是那个等着收 token 的 opener。我盯着日志看了半天,每次都卡在 token 回传那一刻:opener 被自己的恢复逻辑干掉了。

第二,relay 看不见 Google 按钮。 跨域 iframe 在 Chrome 里是个独立的渲染进程(OOPIF)。chrome-use 直连 CDP 时会自动钻进去,看得到 iframe 里真实的「Continue as 立」按钮;relay 模式下,扩展只 attach 到标签顶层,iframe 是个黑盒。我只能拿坐标瞎点,点出来的还是"换个账号"的通用流程,不是那个个性化的一键登录。

第三,relay attach 不上 popup。 那个 Google 账号选择 popup 一开出来,我切过去想点账号,扩展报"标签没响应"——它要等 popup 加载完才慢悠悠去 attach,对一个一闪而过的 OAuth 窗口,太晚了。

还有个隐藏的坑,我排查很久才发现:扩展走的 native-messaging host,指向的是已安装的旧二进制,不是我本地新编的。所以前面那些"打了补丁的测试",跑的全是旧逻辑。把那个 wrapper 脚本指向新编二进制,补丁才真正进了生效路径。

三刀

对着上面三处,各砍一刀。

保住 opener。 会话失效时不再销毁标签,改成非破坏式重连:同一个 targetId 还在(只是 sessionId 轮换了),重新 attach 拿到新 sessionId 就行,不开新标签。读、点这类命令遇到失效会话,也容忍重试几次,等扩展那边把会话恢复过来,而不是第一下就硬报错。

穿透 iframe。 让扩展在 attach 时主动开 Target.setAutoAttach({flatten:true}),把 Google iframe 这个子会话的事件转发给守护进程;命令再按子 sessionId 路由进那个 iframe。这样快照就能走进去,「Continue as 立」拿到一个可点的 ref——和直连 CDP 同等了。

跟住 popup。 扩展用 chrome.tabs.onCreated 在 popup 一创建就 attach(只认 opener 是自己管的标签那种,避免误伤别的窗口),守护进程把它当次级标签收着,但不抢主标签——opener 仍然钉在那,继续等它的 token。

跑通

补丁上去、扩展重载之后,再跑一遍:

  • 快照,GSI iframe 里的「Continue as 立, leeguooooo@gmail.com」露出来了,点它;
  • 弹出账号选择 popup,切过去(这次不报"没响应"了),选账号;
  • 选完什么都不碰,让 gsi/transform 自己把 token postMessage 回 opener;
  • opener 没被销毁,收到 token——x 跳到了 /home,登录态。

全程没有一次人工点击。这块硬骨头,算是啃下来了。

收尾

agent 自己登录账号,到这里两条路都通了:OAuth 这种第三方跳转,chrome-use 自己点完;账号密码、2FA、passkey 这种,bitwarden-use 从你的金库取了填。

← 上一篇
让微信在后台替你发消息,我曾经把它的数据库搞坏 —— 从「挂调试器」到「零附着」
下一篇 →
群晖 DSM 开了 SSH 端口还是打不开:一次端口漂移复盘

评论

评论发布后会立即公开,如触发规则可能被审核下架。

最多 1000 字。