feat(claude): add forward_host_credentials support
Reads the host's Claude OAuth session key from ~/.claude.json at launch and forwards it only to the egress sidecar (never to the agent), placing a placeholder CLAUDE_CODE_OAUTH_TOKEN in the agent env so Claude Code starts without seeing the real credential. Mirrors the existing Codex forward_host_credentials flow (PRD 0029). Adds claude_auth.py to extract and validate the sessionKey, a CLAUDE_HOST_CREDENTIAL_TOKEN_REF constant in egress.py, and updates manifest_agent.py to allow the flag for both 'codex' and 'claude' templates. Also adds a mutual-exclusion check that rejects setting both auth_token and forward_host_credentials together. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> fix(claude): read credentials from ~/.claude/.credentials.json The actual OAuth token is in ~/.claude/.credentials.json under claudeAiOauth.accessToken, not in ~/.claude.json. ~/.claude.json holds only UI state and profile metadata (oauthAccount has no token fields). expiresAt in the credentials file is milliseconds, not seconds. Discovered after testing against Claude Code 2.1.198. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> fix(claude): fall back to macOS Keychain for credentials On macOS, Claude Code stores credentials in the Keychain under service "Claude Code-credentials" rather than in a file. When ~/.claude/.credentials.json is absent, shell out to: security find-generic-password -s "Claude Code-credentials" -w and parse the result as the same JSON schema. ~/.claude.json holds only profile/UI metadata (oauthAccount has no token fields). expiresAt in the credentials is milliseconds. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> docs(prd): fix credential path references (~/.claude/.credentials.json) fix(test): suppress gitleaks false positives on synthetic Claude tokens
This commit is contained in:
@@ -80,11 +80,19 @@ class TestAgentProviderHostCredentials(unittest.TestCase):
|
||||
"forward_host_credentials": "yes",
|
||||
})
|
||||
|
||||
def test_forward_host_credentials_rejected_for_claude(self):
|
||||
def test_forward_host_credentials_allowed_for_claude(self):
|
||||
b = _provider_config_bottle({
|
||||
"template": "claude",
|
||||
"forward_host_credentials": True,
|
||||
})
|
||||
self.assertTrue(b.agent_provider.forward_host_credentials)
|
||||
|
||||
def test_forward_host_credentials_and_auth_token_rejected_together(self):
|
||||
with self.assertRaises(ManifestError):
|
||||
_provider_config_bottle({
|
||||
"template": "claude",
|
||||
"forward_host_credentials": True,
|
||||
"auth_token": "SOME_TOKEN",
|
||||
})
|
||||
|
||||
def test_auth_token_defaults_empty(self):
|
||||
|
||||
Reference in New Issue
Block a user