From c52caa6b17e86946cd8548cd997c2ff8da257fd5 Mon Sep 17 00:00:00 2001 From: AndyMik90 Date: Sat, 20 Dec 2025 18:38:12 +0100 Subject: [PATCH] fix(auth): remove ANTHROPIC_API_KEY fallback to prevent silent billing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove ANTHROPIC_API_KEY from the authentication fallback chain. Auto Claude is designed to use Claude Code OAuth tokens only. Previously, if CLAUDE_CODE_OAUTH_TOKEN was empty or missing, the system would silently fall back to ANTHROPIC_API_KEY from the environment, causing unexpected API billing when users thought they were using OAuth. Closes #76 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- auto-claude/.env.example | 13 +++++++------ auto-claude/cli/utils.py | 15 +++++++-------- auto-claude/core/auth.py | 36 +++++++++++++++++++++++++----------- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/auto-claude/.env.example b/auto-claude/.env.example index a8a02cca..366134a1 100644 --- a/auto-claude/.env.example +++ b/auto-claude/.env.example @@ -2,16 +2,17 @@ # Copy this file to .env and fill in your values # ============================================================================= -# AUTHENTICATION (REQUIRED - set ONE of the following) +# AUTHENTICATION (REQUIRED) # ============================================================================= -# The framework checks these in order of priority: -# 1. CLAUDE_CODE_OAUTH_TOKEN - Original (from `claude setup-token`) -# 2. ANTHROPIC_AUTH_TOKEN - For proxies like CCR -# 3. ANTHROPIC_API_KEY - Direct Anthropic API key +# Auto Claude uses Claude Code OAuth authentication. +# Direct API keys (ANTHROPIC_API_KEY) are NOT supported to prevent silent billing. # +# Option 1: Run `claude setup-token` to save token to macOS Keychain (recommended) +# Option 2: Set the token explicitly: # CLAUDE_CODE_OAUTH_TOKEN=your-oauth-token-here +# +# For enterprise/proxy setups (CCR): # ANTHROPIC_AUTH_TOKEN=sk-zcf-x-ccr -# ANTHROPIC_API_KEY=sk-ant-... # ============================================================================= # CUSTOM API ENDPOINT (OPTIONAL) diff --git a/auto-claude/cli/utils.py b/auto-claude/cli/utils.py index cc4055cb..23ed8e8d 100644 --- a/auto-claude/cli/utils.py +++ b/auto-claude/cli/utils.py @@ -96,20 +96,19 @@ def validate_environment(spec_dir: Path) -> bool: """ valid = True - # Check for authentication token (supports multiple env vars) + # Check for OAuth token (API keys are not supported) if not get_auth_token(): - print("Error: No authentication token found") - print(f"\nSet one of: {', '.join(AUTH_TOKEN_ENV_VARS)}") - print("\nFor Claude Code CLI, get your OAuth token by running:") + print("Error: No OAuth token found") + print("\nAuto Claude requires Claude Code OAuth authentication.") + print("Direct API keys (ANTHROPIC_API_KEY) are not supported.") + print("\nTo authenticate, run:") print(" claude setup-token") - print("\nThen set it:") - print(" export CLAUDE_CODE_OAUTH_TOKEN='your-token-here'") valid = False else: # Show which auth source is being used source = get_auth_token_source() - if source and source != "CLAUDE_CODE_OAUTH_TOKEN": - print(f"Auth: Using token from {source}") + if source: + print(f"Auth: {source}") # Show custom base URL if set base_url = os.environ.get("ANTHROPIC_BASE_URL") diff --git a/auto-claude/core/auth.py b/auto-claude/core/auth.py index 76e54eb5..ae0c6bae 100644 --- a/auto-claude/core/auth.py +++ b/auto-claude/core/auth.py @@ -12,17 +12,19 @@ import platform import subprocess # Priority order for auth token resolution +# NOTE: We intentionally do NOT fall back to ANTHROPIC_API_KEY. +# Auto Claude is designed to use Claude Code OAuth tokens only. +# This prevents silent billing to user's API credits when OAuth fails. AUTH_TOKEN_ENV_VARS = [ - "CLAUDE_CODE_OAUTH_TOKEN", # Original (highest priority) - "ANTHROPIC_AUTH_TOKEN", # CCR/proxy token - "ANTHROPIC_API_KEY", # Direct API key (lowest priority) + "CLAUDE_CODE_OAUTH_TOKEN", # OAuth token from Claude Code CLI + "ANTHROPIC_AUTH_TOKEN", # CCR/proxy token (for enterprise setups) ] # Environment variables to pass through to SDK subprocess +# NOTE: ANTHROPIC_API_KEY is intentionally excluded to prevent silent API billing SDK_ENV_VARS = [ "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN", - "ANTHROPIC_API_KEY", "NO_PROXY", "DISABLE_TELEMETRY", "DISABLE_COST_WARNINGS", @@ -92,9 +94,11 @@ def get_auth_token() -> str | None: Checks multiple sources in priority order: 1. CLAUDE_CODE_OAUTH_TOKEN (env var) - 2. ANTHROPIC_AUTH_TOKEN (ccr/proxy env var) - 3. ANTHROPIC_API_KEY (direct API key env var) - 4. macOS Keychain (if on Darwin platform) + 2. ANTHROPIC_AUTH_TOKEN (CCR/proxy env var for enterprise setups) + 3. macOS Keychain (if on Darwin platform) + + NOTE: ANTHROPIC_API_KEY is intentionally NOT supported to prevent + silent billing to user's API credits when OAuth is misconfigured. Returns: Token string if found, None otherwise @@ -133,14 +137,24 @@ def require_auth_token() -> str: token = get_auth_token() if not token: error_msg = ( - "No authentication token found.\n" - f"Set one of: {', '.join(AUTH_TOKEN_ENV_VARS)}\n" + "No OAuth token found.\n\n" + "Auto Claude requires Claude Code OAuth authentication.\n" + "Direct API keys (ANTHROPIC_API_KEY) are not supported.\n\n" ) # Provide platform-specific guidance if platform.system() == "Darwin": - error_msg += "For Claude Code CLI: run 'claude setup-token' to save token to macOS Keychain" + error_msg += ( + "To authenticate:\n" + " 1. Run: claude setup-token\n" + " 2. The token will be saved to macOS Keychain automatically\n\n" + "Or set CLAUDE_CODE_OAUTH_TOKEN in your .env file." + ) else: - error_msg += "For Claude Code CLI: run 'claude setup-token'" + error_msg += ( + "To authenticate:\n" + " 1. Run: claude setup-token\n" + " 2. Set CLAUDE_CODE_OAUTH_TOKEN in your .env file" + ) raise ValueError(error_msg) return token