External Credential Sync
Clawdbot Contributors· validated-in-production
问题
用户需要在多种工具中管理AI API凭证,包括CLI(如Claude Code、Codex CLI)、Web门户以及本地开发环境。手动为每个工具重复输入凭证不仅操作繁琐,还会引发以下问题:
- 过期令牌:OAuth刷新令牌过期,导致身份验证失败
- 状态不一致:在某一工具中更新的凭证无法同步至其他工具
- 令牌机制差异漂移:部分工具支持OAuth刷新功能,而其他工具存储的是会过期的静态令牌
Agent需要具备凭证自动同步能力,在降低身份验证操作复杂度的同时保障安全性(如不存储明文凭证、妥善处理令牌过期等)。
方案
支持近过期检测、类型感知升级与重复项检测的跨凭证源同步机制。该系统从外部工具存储(钥匙串、配置文件)中读取凭证,通过智能合并和新鲜度跟踪,将其同步至Agent的凭证存储(Credential Store)中。
核心概念:
- 源插件(Source plugins):每个外部工具(Claude CLI、Codex CLI、通义千问门户)都实现了一个凭证读取器,用于访问其安全存储(钥匙串、加密配置)。
- 近过期检测(Near-expiry detection):距离过期不足24小时的凭证会触发主动刷新,避免在活跃会话中出现认证失败。
- 类型感知升级(Type-aware upgrades):优先采用OAuth凭证而非仅令牌型凭证。当同步流程检测到某一配置文件(Profile)原本使用仅令牌凭证,但存在对应的OAuth凭证时,会自动升级以启用自动刷新功能。
- 重复项检测(Duplicate detection):通过对比凭证值(访问令牌、刷新令牌),避免为同一底层账户创建重复的配置文件。
- 基于TTL的缓存(TTL-based caching):外部读取的结果会被缓存5分钟,既减少对钥匙串的频繁访问,又能保证凭证的新鲜度。
- 不可变配置文件ID(Immutable profile IDs):每个外部源对应一个固定的配置文件ID(例如
claude-cli、codex-cli),确保在多次同步周期内都能稳定引用。
实现概要:
const EXTERNAL_CLI_NEAR_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24小时
const EXTERNAL_CLI_SYNC_TTL_MS = 5 * 60 * 1000; // 5分钟缓存
/**
* 判断外部配置文件凭证是否处于新鲜状态
* @param cred 待检查的凭证
* @param now 当前时间戳(毫秒)
* @returns 是否新鲜
*/
function isExternalProfileFresh(cred: Credential, now: number): boolean {
if (cred.type !== "oauth" && cred.type !== "token") return false;
if (!["anthropic", "openai-codex", "qwen-portal"].includes(cred.provider)) return false;
if (typeof cred.expires !== "number") return true; // 无过期时间则视为新鲜
return cred.expires > now + EXTERNAL_CLI_NEAR_EXPIRY_MS;
}
/**
* 同步外部CLI工具的凭证
* @param store 凭证存储实例
* @returns 是否有凭证变更
*/
function syncExternalCliCredentials(store: CredentialStore): boolean {
let mutated = false;
const now = Date.now();
// 从Claude Code CLI同步凭证
const existingClaude = store.profiles["claude-cli"];
const shouldSyncClaude = !existingClaude || !isExternalProfileFresh(existingClaude, now);
if (shouldSyncClaude) {
const claudeCreds = readClaudeCliCredentialsCached({
ttlMs: EXTERNAL_CLI_SYNC_TTL_MS,
});
if (claudeCreds) {
// 若CLI已升级为OAuth凭证,则将原有令牌凭证升级
const shouldUpgrade = existingClaude?.type === "token" && claudeCreds.type === "oauth";
const isMoreRecent = claudeCreds.expires > (existingClaude?.expires ?? 0);
if (shouldUpgrade || isMoreRecent) {
store.profiles["claude-cli"] = claudeCreds;
mutated = true;
}
}
}
// 为其他源(Codex CLI、通义千问门户)重复上述同步逻辑...
return mutated;
}
Codex CLI重复项检测:
/**
* 在凭证存储中查找与给定Codex OAuth凭证重复的配置文件
* @param store 凭证存储实例
* @param creds 待检测的Codex OAuth凭证
* @returns 重复配置文件的ID(若存在)
*/
function findDuplicateCodexProfile(store: CredentialStore, creds: OAuthCredential): string | undefined {
for (const [profileId, profile] of Object.entries(store.profiles)) {
if (profileId === "codex-cli") continue;
if (profile.provider !== "openai-codex") continue;
if (profile.access === creds.access && profile.refresh === creds.refresh) {
return profileId; // 同一凭证已存在于其他配置文件下
}
}
return undefined;
}
类型升级优先级逻辑:
// 优先选用OAuth凭证而非仅令牌凭证(支持自动刷新)
if (existing?.type === "token" && claudeCreds.type === "oauth") {
store.profiles[CLAUDE_CLI_PROFILE_ID] = claudeCreds;
mutated = true;
}
// 绝不从OAuth凭证降级为仅令牌凭证
if (existing?.type === "oauth" && claudeCreds.type === "token") {
// 跳过更新,保留OAuth的自动刷新能力
}
如何使用
核心操作步骤
- 识别凭证源:映射存储同一服务提供商(Anthropic、OpenAI 等)凭证的外部工具。
- 实现凭证读取器:针对每个凭证源,编写函数从其安全存储(钥匙串、配置文件)中读取凭证。
- 定义配置文件ID:为每个外部源分配稳定的标识ID(例如
claude-cli、codex-cli)。 - 启动触发+定时同步:在Agent启动时执行同步操作,并定期(例如每小时一次)运行同步任务,以刷新即将过期的凭证。
- 处理升级路径:当仅支持令牌的配置文件可使用OAuth时,自动完成升级。
- 检测重复项:创建新配置文件前,检查是否已存在凭证值完全相同的现有配置文件。
需规避的陷阱:
- 过度读取钥匙串:对外部读取操作进行缓存,避免过于频繁触发操作系统安全提示。
- 遗漏过期处理:部分凭证未携带过期信息(如Codex CLI),可将文件修改时间作为启发式判断依据。
- OAuth降级风险:绝不能用仅令牌凭证替换OAuth凭证,此举会丢失自动刷新能力。
- 竞态条件:多个同步操作并发运行可能覆盖凭证,需使用文件锁或互斥锁规避该问题。
权衡
优点:
- 降低操作摩擦:用户针对每个服务提供商仅需完成一次认证,所有工具均可从中受益。
- 主动刷新机制:通过临近过期检测,可避免活跃会话过程中出现认证失败的情况。
- 自动类型升级:当工具从仅支持token的模式升级为OAuth模式时,OAuth的适配会自动完成。
- 消除冗余:避免在凭证存储中堆积冗余配置文件。
缺点/注意事项:
- 依赖系统钥匙串:需要访问操作系统的钥匙串服务,这在无界面(headless)环境中可能失效。
- 平台API差异:Windows、macOS和Linux的钥匙串API各不相同,需进行抽象处理以实现多平台兼容。
- 权限要求:读取钥匙串凭证可能需要用户授权或提升系统权限。
- 缓存同步延迟:缓存读取设置了5分钟TTL,新凭证可能无法立即在系统中生效。
参考文献
关键词:
涉及Clawdbot项目中认证配置文件同步逻辑、CLI凭据读取(钥匙串访问)、凭据类型定义的核心代码文件,以及关联的个人身份信息(PII)令牌化凭据安全模式。
- Clawdbot CLI 凭据读取器 - 钥匙串访问(Keychain)
- Clawdbot 凭据类型定义文件 - 类型定义
相关内容:个人身份信息(PII)令牌化——用于凭据安全模式
来源摘要
正在获取来源并生成中文摘要…
来源: https://github.com/clawdbot/clawdbot/blob/main/src/agents/auth-profiles/external-cli-sync.ts