gogcli `gog auth tokens import` 命令详解:把刷新令牌安全导入系统钥匙串
2026/9/16 17:21:31 网站建设 项目流程

gogcligog auth tokens import命令详解:把刷新令牌安全导入系统钥匙串

【免费下载链接】gogcliGoogle Workspace in your terminal.项目地址: https://gitcode.com/GitHub_Trending/gogcl/gogcli

导读

gog auth tokens import是 gogcli(Google Workspace in your terminal)用于把**包含 OAuth 刷新令牌(refresh token)的 JSON 文件导入系统钥匙串(keyring)**的命令。它通常与gog auth tokens export配对使用,实现跨机器迁移账号、备份恢复或将令牌文件批量注入 gogcli 的凭据库。读完本文,你将掌握该命令的完整用法、JSON 文件格式规范、校验规则、client 解析逻辑,以及它背后的源码实现与测试验证方式。

命令总览

gog auth tokens import属于gog auth tokens子命令族(gog-auth-tokens.md),同族还包括:

  • gog auth tokens list— 列出已存储的令牌(仅键名)
  • gog auth tokens delete— 删除一个已存储的刷新令牌
  • gog auth tokens export— 把刷新令牌导出到文件(含机密)

其基本用法(gog-auth-tokens-import.md):

gog auth tokens import <inPath>

<inPath>是必选位置参数,指向包含令牌信息的 JSON 文件路径;传入-时则从标准输入读取。命令成功后在标准错误输出一行提示Imported refresh token into keyring,表示令牌已写入系统钥匙串。

输入文件格式:与 export 完全互通的 JSON 结构

import 命令读取的 JSON 结构与gog auth tokens export导出的文件结构完全一致,因此一条命令导出的文件可以直接用另一条命令原样导回(export 与 import 共用同一份结构定义,见 internal/cmd/auth_tokens.go 与 internal/cmd/auth_tokens.go)。完整字段如下:

字段类型是否必填说明
emailstring必填账号邮箱,导入时会做 trim 处理,为空则报错
refresh_tokenstring必填OAuth 刷新令牌,为空则报错
subjectstring可选模拟用户(service account 场景下的域内用户)
clientstring可选OAuth client 名称,用于选择凭据与令牌桶
services[]string可选该令牌授权的服务列表
scopes[]string可选OAuth scope 列表
created_atstring可选令牌创建时间,须为 RFC3339 格式,否则报错
access_tokenstring可选随附的访问令牌(有效约 1 小时)
access_token_expires_atstring可选访问令牌过期时间,须为 RFC3339 格式

一个最小可用的导入文件示例:

{ "email": "me@example.com", "refresh_token": "1//0xxxxxxxxxxxxxxxxxxxxxxxxx" }

gog auth tokens export生成的完整示例(导出自 internal/cmd/auth_tokens.go 的export结构,字段顺序与 JSON 标签保持一致):

{ "email": "me@example.com", "subject": "", "client": "gog", "services": ["gmail", "calendar"], "scopes": ["https://www.googleapis.com/auth/gmail.modify"], "created_at": "2025-01-01T00:00:00Z", "refresh_token": "1//0xxxxxxxxxxxxxxxxxxxxxxxxx", "access_token": "", "access_token_expires_at": "" }

注意:导入时created_ataccess_token_expires_at两个时间字段通过time.Parse(time.RFC3339, ...)解析,任何非 RFC3339 的值(例如"bad")都会直接导致导入失败。

从标准输入读取

inPath传入-时,命令从 stdin 读取完整 JSON 内容,适用于管道场景:

cat token.json | gog auth tokens import -

该路径在源码中通过io.ReadAll(stdinReader(ctx))实现(internal/cmd/auth_tokens.go),并有对应的 stdin 导入测试用例(见下文测试章节)。

client 解析逻辑:flag 优先,文件字段兜底

导入文件中的client字段并不是必填项。当文件未指定 client 时,命令通过resolveClientForEmailWithContext结合全局--clientflag 与已存储凭据决定最终 client:

  1. 若全局 flag 显式指定了--client(即上下文中的ClientOverride),优先使用该值;
  2. 否则回退到 JSON 文件中的client字段;
  3. 都没有时,交由解析函数根据邮箱与已配置凭据推导默认值(通常为默认 client 名,测试与配置中可见config.DefaultClientName)。

对应逻辑位于 internal/cmd/auth_tokens.go。这意味着同一个邮箱可以在不同 client 下各自保存一份令牌——测试 auth_tokens_more_test.go 验证了composeinboxrorw四个 client 下同一邮箱互不覆盖。

校验规则与错误处理

import 在写入钥匙串之前执行严格的前置校验,全部校验失败都以 usage 类错误(exit code 2)返回:

校验点失败消息(来自源码)
JSON 无法解析invalid token JSON: <err>
email为空missing email in token file
refresh_token为空missing refresh_token in token file
created_at非 RFC3339invalid created_at "<val>" (expected RFC3339)
access_token_expires_at非 RFC3339invalid access_token_expires_at "<val>" (expected RFC3339)
文件不存在/不可读读取错误直接返回

校验顺序在源码中非常清晰(internal/cmd/auth_tokens.go):先反序列化 JSON,再依次校验 email、refresh_token,最后解析两个时间字段。注意时间字段即使传入--dry-run也会先被校验,测试用例对此有专门断言(见下文)。

全局 Flags 速查

gog auth tokens import不定义专属 flag,但继承全部全局 flag(见 gog-auth-tokens-import.md 的 Flags 表):

Flag类型默认作用
--access-tokenstring直接使用提供的访问令牌(绕过已存储刷新令牌;令牌约 1 小时过期)
-a/--account/--acctstring账号邮箱、别名或 auto(用于需认证的 Google API 命令)
--clientstringOAuth client 名称(选择已存凭据 + 令牌桶)
--colorstringauto颜色输出:auto|always|never
--disable-commandsstring禁用的命令列表(逗号分隔,支持点路径)
-n/--dry-run/--dryrun/--noop/--previewbool不实际改动,仅打印计划执行的动作并成功退出
--enable-commandsstring启用的命令前缀列表(逗号分隔,支持点路径,可收窄 CLI)
--enable-commands-exactstring精确启用的命令列表(点路径;父命令不会连带启用子命令)
-y/--force/--assume-yes/--yesbool跳过破坏性命令的确认
--gmail-no-sendboolfalse阻止 Gmail 发送操作(Agent 安全)
-h/--helpkong.helpFlag显示上下文相关帮助
--homestring覆盖 gogcli 配置/数据/状态/缓存根目录(等价于GOG_HOME
-j/--json/--machineboolfalse向 stdout 输出 JSON(适合脚本化)
--no-input/--non-interactive/--noninteractivebool永不提示,失败即退出(适合 CI)
-p/--plain/--tsvboolfalse向 stdout 输出稳定可解析文本(TSV,无颜色)
--quota-projectstring用于计费 API 用量的 Google Cloud 项目(以X-Goog-User-Project头发送;部分 API 在--access-token/ADC 下必须)
--readonlyboolfalse运行时阻止变更类 API 请求;auth add也只申请只读 scope
--results-onlyboolJSON 模式下只输出主结果(丢弃 nextPageToken 等信封字段)
--select/--pick/--projectstringJSON 模式下选择逗号分隔字段(尽力而为,支持点路径)
-v/--verbosebool开启详细日志
--versionkong.VersionFlag打印版本并退出
--wrap-untrustedboolfalseJSON/raw 输出中为外部抓取的文本字段包裹不可信内容标记

输出格式

  • 默认文本模式输出三行 TSV 风格结果(stdout):imported\ttrueemail\t<邮箱>client\t<client>
  • -j/--json时输出 JSON 对象:{"imported": true, "email": "...", "client": "..."}
  • 提示信息Imported refresh token into keyring写入stderr(internal/cmd/auth_tokens.go),避免污染 stdout 的机器可读输出。

实操场景:迁移、备份与 CI

场景一:跨机器迁移账号

在一台已登录的机器上导出,再到新机器导入:

# 机器 A:导出令牌文件 gog auth tokens export me@example.com --out token.json --overwrite # 机器 B:导入令牌 gog auth tokens import token.json gog auth list # 确认账号已可用

导出命令写入文件时使用0o600文件权限、0o700目录权限(internal/cmd/auth_tokens.go),导入端则复用同一 JSON 结构,确保来回可迁移。测试 auth_tokens_more_test.go 的TestAuthTokensExportImport_JSON完整演示了「导出到临时目录 → 读回校验refresh_token→ 导入到全新 store → 再取回比对」的往返闭环。

场景二:dry-run 预演

导入属于写操作,正式执行前可用-n预览:

gog auth tokens import token.json -n

dry-run 会把将要写入的信息(email、client、subject、services、scopes、created_at、refresh_token 是否提供、access_token 相关字段)打印出来而不落盘(internal/cmd/auth_tokens.go)。端到端测试 dryrun_e2e_test.go 将auth.tokens.import列为受支持的 dry-run 操作之一。

场景三:CI/脚本中安全导入

令牌文件本身即机密,建议结合--no-input与文件权限控制:

gog auth tokens import /path/to/token.json --no-input -j

--no-input保证在需要交互确认时直接失败而不是挂起;-j便于脚本解析imported/email/client字段。

源码实现要点

  • 命令注册AuthTokensCmd在 internal/cmd/auth_tokens.go 中以name:"import"注册AuthTokensImportCmd,help 文案即「Import a refresh token file into keyring (contains secrets)」。
  • 结构定义AuthTokensImportCmd仅含一个位置参数InPatharg:"" name:"inPath",支持-表示 stdin,见 internal/cmd/auth_tokens.go)。
  • 密钥落点:导入最终调用store.SetToken(client, email, secrets.Token{...}),底层是KeyringStore(基于github.com/99designs/keyring),令牌以client:email为键存储(键生成见 internal/secrets/token.go 的TokenKey);写入前还会调用ensureKeychainAccessIfNeeded确认钥匙串可达(internal/cmd/auth_tokens.go)。
  • 落库前补默认值:若导入文件未携带created_atKeyringStore.setTokenNoLock会自动补为当前 UTC 时间(internal/secrets/token.go);email会做 normalize 处理,refresh_token为空同样被拒绝(internal/secrets/token.go)。
  • Token 模型secrets.TokenRefreshTokenAccessTokenAccessTokenExpiresAt三个字段 JSON 序列化时被显式排除(json:"-",见 internal/secrets/token.go),避免任何日志/序列化路径意外泄露令牌明文。

测试与质量保障

仓库为 import 命令提供了多层测试证据:

  • 往返一致性:auth_tokens_more_test.go 验证 export 产物能被 import 完整还原;
  • 异常路径全覆盖:auth_validation_more_test.go 的TestAuthTokensImport_ErrorsAndStdin覆盖了:不存在的文件路径、非法 JSON、缺失 email、非法created_at(含 dry-run 下同样报错)、非法access_token_expires_at、以及 stdin 导入成功路径;
  • dry-run 集成:dryrun_e2e_test.go 把gog auth tokens import <tokenPath>注册进 dry-run 端到端清单,操作名auth.tokens.import

安全注意事项

  1. 令牌文件即机密refresh_token是长期有效的凭据,导出文件务必妥善保管、用后即删(export 命令本身也会在 stderr 输出警告:WARNING: exported file contains OAuth tokens (keep it safe and delete it when done),见 internal/cmd/auth_tokens.go)。
  2. 文件权限:建议沿用 export 的0o600权限约定;导入时避免从不可信来源读取令牌文件。
  3. 最小暴露--json/--plain输出仅包含 imported/email/client 元数据,不会回显令牌本身。
  4. 配合只读策略:在受管控环境可结合--readonly--enable-commands等全局安全 flag 限制命令面(相关策略可参考 safety-profiles/agent-safe.yaml 等安全配置)。

关联文档与延伸阅读

  • 父命令:gog auth tokens
  • 配套导出:gog auth tokens export
  • 配套删除/列举:gog auth tokens delete、gog auth tokens list
  • 命令总索引:Command index
  • 账号管理入口:gog auth manage

【免费下载链接】gogcliGoogle Workspace in your terminal.项目地址: https://gitcode.com/GitHub_Trending/gogcl/gogcli

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询