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-clicodex-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的自动刷新能力
}

如何使用

核心操作步骤

  1. 识别凭证源:映射存储同一服务提供商(Anthropic、OpenAI 等)凭证的外部工具。
  2. 实现凭证读取器:针对每个凭证源,编写函数从其安全存储(钥匙串、配置文件)中读取凭证。
  3. 定义配置文件ID:为每个外部源分配稳定的标识ID(例如 claude-clicodex-cli)。
  4. 启动触发+定时同步:在Agent启动时执行同步操作,并定期(例如每小时一次)运行同步任务,以刷新即将过期的凭证。
  5. 处理升级路径:当仅支持令牌的配置文件可使用OAuth时,自动完成升级。
  6. 检测重复项:创建新配置文件前,检查是否已存在凭证值完全相同的现有配置文件。

需规避的陷阱:

  • 过度读取钥匙串:对外部读取操作进行缓存,避免过于频繁触发操作系统安全提示。
  • 遗漏过期处理:部分凭证未携带过期信息(如Codex CLI),可将文件修改时间作为启发式判断依据。
  • OAuth降级风险:绝不能用仅令牌凭证替换OAuth凭证,此举会丢失自动刷新能力。
  • 竞态条件:多个同步操作并发运行可能覆盖凭证,需使用文件锁或互斥锁规避该问题。

权衡

优点:

  • 降低操作摩擦:用户针对每个服务提供商仅需完成一次认证,所有工具均可从中受益。
  • 主动刷新机制:通过临近过期检测,可避免活跃会话过程中出现认证失败的情况。
  • 自动类型升级:当工具从仅支持token的模式升级为OAuth模式时,OAuth的适配会自动完成。
  • 消除冗余:避免在凭证存储中堆积冗余配置文件。

缺点/注意事项:

  • 依赖系统钥匙串:需要访问操作系统的钥匙串服务,这在无界面(headless)环境中可能失效。
  • 平台API差异:Windows、macOS和Linux的钥匙串API各不相同,需进行抽象处理以实现多平台兼容。
  • 权限要求:读取钥匙串凭证可能需要用户授权或提升系统权限。
  • 缓存同步延迟:缓存读取设置了5分钟TTL,新凭证可能无法立即在系统中生效。

参考文献

关键词

涉及Clawdbot项目中认证配置文件同步逻辑、CLI凭据读取(钥匙串访问)、凭据类型定义的核心代码文件,以及关联的个人身份信息(PII)令牌化凭据安全模式。

相关内容:个人身份信息(PII)令牌化——用于凭据安全模式

来源摘要

正在获取来源并生成中文摘要…

来源: https://github.com/clawdbot/clawdbot/blob/main/src/agents/auth-profiles/external-cli-sync.ts

← 返回社区