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)。完整字段如下:
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
email | string | 必填 | 账号邮箱,导入时会做 trim 处理,为空则报错 |
refresh_token | string | 必填 | OAuth 刷新令牌,为空则报错 |
subject | string | 可选 | 模拟用户(service account 场景下的域内用户) |
client | string | 可选 | OAuth client 名称,用于选择凭据与令牌桶 |
services | []string | 可选 | 该令牌授权的服务列表 |
scopes | []string | 可选 | OAuth scope 列表 |
created_at | string | 可选 | 令牌创建时间,须为 RFC3339 格式,否则报错 |
access_token | string | 可选 | 随附的访问令牌(有效约 1 小时) |
access_token_expires_at | string | 可选 | 访问令牌过期时间,须为 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_at与access_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:
- 若全局 flag 显式指定了
--client(即上下文中的ClientOverride),优先使用该值; - 否则回退到 JSON 文件中的
client字段; - 都没有时,交由解析函数根据邮箱与已配置凭据推导默认值(通常为默认 client 名,测试与配置中可见
config.DefaultClientName)。
对应逻辑位于 internal/cmd/auth_tokens.go。这意味着同一个邮箱可以在不同 client 下各自保存一份令牌——测试 auth_tokens_more_test.go 验证了compose、inbox、ro、rw四个 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非 RFC3339 | invalid created_at "<val>" (expected RFC3339) |
access_token_expires_at非 RFC3339 | invalid 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-token | string | 直接使用提供的访问令牌(绕过已存储刷新令牌;令牌约 1 小时过期) | |
-a/--account/--acct | string | 账号邮箱、别名或 auto(用于需认证的 Google API 命令) | |
--client | string | OAuth client 名称(选择已存凭据 + 令牌桶) | |
--color | string | auto | 颜色输出:auto|always|never |
--disable-commands | string | 禁用的命令列表(逗号分隔,支持点路径) | |
-n/--dry-run/--dryrun/--noop/--preview | bool | 不实际改动,仅打印计划执行的动作并成功退出 | |
--enable-commands | string | 启用的命令前缀列表(逗号分隔,支持点路径,可收窄 CLI) | |
--enable-commands-exact | string | 精确启用的命令列表(点路径;父命令不会连带启用子命令) | |
-y/--force/--assume-yes/--yes | bool | 跳过破坏性命令的确认 | |
--gmail-no-send | bool | false | 阻止 Gmail 发送操作(Agent 安全) |
-h/--help | kong.helpFlag | 显示上下文相关帮助 | |
--home | string | 覆盖 gogcli 配置/数据/状态/缓存根目录(等价于GOG_HOME) | |
-j/--json/--machine | bool | false | 向 stdout 输出 JSON(适合脚本化) |
--no-input/--non-interactive/--noninteractive | bool | 永不提示,失败即退出(适合 CI) | |
-p/--plain/--tsv | bool | false | 向 stdout 输出稳定可解析文本(TSV,无颜色) |
--quota-project | string | 用于计费 API 用量的 Google Cloud 项目(以X-Goog-User-Project头发送;部分 API 在--access-token/ADC 下必须) | |
--readonly | bool | false | 运行时阻止变更类 API 请求;auth add也只申请只读 scope |
--results-only | bool | JSON 模式下只输出主结果(丢弃 nextPageToken 等信封字段) | |
--select/--pick/--project | string | JSON 模式下选择逗号分隔字段(尽力而为,支持点路径) | |
-v/--verbose | bool | 开启详细日志 | |
--version | kong.VersionFlag | 打印版本并退出 | |
--wrap-untrusted | bool | false | JSON/raw 输出中为外部抓取的文本字段包裹不可信内容标记 |
输出格式
- 默认文本模式输出三行 TSV 风格结果(stdout):
imported\ttrue、email\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 -ndry-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仅含一个位置参数InPath(arg:"" 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_at,KeyringStore.setTokenNoLock会自动补为当前 UTC 时间(internal/secrets/token.go);email会做 normalize 处理,refresh_token为空同样被拒绝(internal/secrets/token.go)。 - Token 模型:
secrets.Token中RefreshToken、AccessToken、AccessTokenExpiresAt三个字段 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。
安全注意事项
- 令牌文件即机密:
refresh_token是长期有效的凭据,导出文件务必妥善保管、用后即删(export 命令本身也会在 stderr 输出警告:WARNING: exported file contains OAuth tokens (keep it safe and delete it when done),见 internal/cmd/auth_tokens.go)。 - 文件权限:建议沿用 export 的
0o600权限约定;导入时避免从不可信来源读取令牌文件。 - 最小暴露:
--json/--plain输出仅包含 imported/email/client 元数据,不会回显令牌本身。 - 配合只读策略:在受管控环境可结合
--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),仅供参考