Skip to content

Validate Claude Code OAuth imports - #98

Draft
k2v7n24cbf-cyber wants to merge 1 commit into
sandydasari:mainfrom
k2v7n24cbf-cyber:agent/claude-oauth-stale-credentials
Draft

Validate Claude Code OAuth imports#98
k2v7n24cbf-cyber wants to merge 1 commit into
sandydasari:mainfrom
k2v7n24cbf-cyber:agent/claude-oauth-stale-credentials

Conversation

@k2v7n24cbf-cyber

Copy link
Copy Markdown

Summary

This PR hardens the Anthropic Claude Code OAuth import/recovery path so OpenAcme does not treat stale Claude Code credentials as a successful login.

Changes:

  • Validate Claude Code credentials during explicit import/login before reporting success.
  • Refresh expired Claude Code credentials immediately when possible.
  • Restore the previous OpenAcme auth entry if an imported Claude Code credential is rejected during validation.
  • Prevent silent background reimport from writing already-expired Claude Code credentials.
  • Compare full Claude Code auth material (access_token, refresh_token, expires_at, account_id, mode) instead of comparing only access_token.
  • Retry Anthropic refresh recovery when Claude Code rotated refresh metadata but kept the same access token.
  • Surface unusable Claude Code credentials in CLI/setup flows and fall back to setup-token entry instead of presenting a misleading successful import.

Observation

While testing a local OpenAcme deployment using Claude subscription auth, OpenAcme continued failing Anthropic refresh with invalid_grant even after the Claude account had recently been refreshed/logged in.

The local credential inspection was redacted, but the shape was important:

  • OpenAcme's stored Anthropic entry was mode: claude-code and had an expired expires_at.
  • Claude's account/profile metadata in ~/.claude.json had recent activity.
  • The token object OpenAcme actually imports (claudeAiOauth) was not present in that recent metadata path.
  • OpenAcme then fell back to the macOS Keychain item Claude Code-credentials, which still contained the same stale OAuth material.

So the user-visible state looked like "Claude was refreshed recently", but OpenAcme was importing a stale credential source and accepting it as valid.

Root Cause

There were three related issues in the Claude Code credential handling:

  1. loginWithClaudeCodeCredentials() blindly imported whatever Claude Code credential was found and returned success. It did not validate whether the credential was already expired or whether its refresh token was still accepted by Anthropic.

  2. tryReimportClaudeCode() was designed for silent recovery, but it could silently write expired Claude Code credentials from the keychain. That made recovery loops reload stale material instead of forcing a useful relogin/setup-token path.

  3. Reimport idempotence compared only access_token. If Claude Code rotated refresh_token or expiry metadata while the access token string stayed the same, OpenAcme considered the credential unchanged and kept the stale refresh metadata.

Impact

After this change:

  • Explicit Claude Code import only succeeds when the imported credential is usable or can be refreshed.
  • Expired/rejected Claude Code credentials no longer replace a previous working auth entry.
  • Silent recovery avoids importing obviously expired keychain material.
  • Refresh-token and expiry rotations are picked up even when the bearer token string is unchanged.
  • CLI/setup users get a clear "Claude Code credentials unusable" path and can paste a setup token instead of being told the import succeeded.

Validation

Local validation run by git hooks and manually while developing:

  • pnpm --filter @openacme/auth test
  • pnpm --filter @openacme/auth check-types
  • pnpm --filter @openacme/cli check-types
  • pnpm --filter @openacme/server check-types
  • pnpm --filter @openacme/llm-provider check-types
  • pnpm check-types via pre-commit hook
  • pnpm test via pre-commit hook
  • pnpm build via pre-push hook
  • pnpm test:e2e via pre-push hook

Also verified in a local production-style OpenAcme install that the patched daemon starts and responds on /api/health after replacing the live bundle.

k2v7n24cbf-cyber added a commit to k2v7n24cbf-cyber/openacme that referenced this pull request Jul 22, 2026
Merge staged copy of sandydasari#98 into local-stage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant