diff --git a/agent/anthropic_adapter.py b/agent/anthropic_adapter.py index e842d3eebf..630656a2ba 100644 --- a/agent/anthropic_adapter.py +++ b/agent/anthropic_adapter.py @@ -161,18 +161,27 @@ def _get_claude_code_version() -> str: def _is_oauth_token(key: str) -> bool: - """Check if the key is an OAuth/setup token (not a regular Console API key). + """Check if the key is an Anthropic OAuth/setup token. - Regular API keys start with 'sk-ant-api'. Everything else (setup-tokens - starting with 'sk-ant-oat', managed keys, JWTs, etc.) needs Bearer auth. + Positively identifies Anthropic OAuth tokens by their key format: + - ``sk-ant-`` prefix (but NOT ``sk-ant-api``) → setup tokens, managed keys + - ``eyJ`` prefix → JWTs from the Anthropic OAuth flow + + Non-Anthropic keys (MiniMax, Alibaba, etc.) don't match either pattern + and correctly return False. """ if not key: return False - # Regular Console API keys use x-api-key header + # Regular Anthropic Console API keys — x-api-key auth, never OAuth if key.startswith("sk-ant-api"): return False - # Everything else (setup-tokens, managed keys, JWTs) uses Bearer auth - return True + # Anthropic-issued tokens (setup-tokens sk-ant-oat-*, managed keys) + if key.startswith("sk-ant-"): + return True + # JWTs from Anthropic OAuth flow + if key.startswith("eyJ"): + return True + return False def _normalize_base_url_text(base_url) -> str: diff --git a/run_agent.py b/run_agent.py index db744019c6..3d0c0d3388 100644 --- a/run_agent.py +++ b/run_agent.py @@ -4425,7 +4425,7 @@ class AIAgent: self._anthropic_api_key = runtime_key self._anthropic_base_url = runtime_base self._anthropic_client = build_anthropic_client(runtime_key, runtime_base) - self._is_anthropic_oauth = _is_oauth_token(runtime_key) if self.provider == "anthropic" else False + self._is_anthropic_oauth = _is_oauth_token(runtime_key) self.api_key = runtime_key self.base_url = runtime_base return diff --git a/tests/agent/test_anthropic_adapter.py b/tests/agent/test_anthropic_adapter.py index 0c91c58019..ae78888d86 100644 --- a/tests/agent/test_anthropic_adapter.py +++ b/tests/agent/test_anthropic_adapter.py @@ -39,8 +39,13 @@ class TestIsOAuthToken: assert _is_oauth_token("sk-ant-api03-abcdef1234567890") is False def test_managed_key(self): - # Managed keys from ~/.claude.json are NOT regular API keys - assert _is_oauth_token("ou1R1z-ft0A-bDeZ9wAA") is True + # Managed keys from ~/.claude.json without a recognisable Anthropic + # prefix are not positively identified as OAuth. They enter the system + # via diagnostics-only read_claude_managed_key(), not via + # resolve_anthropic_token(), so they don't reach the OAuth gate in + # practice. Third-party provider keys (MiniMax, Alibaba) also lack + # the sk-ant- prefix and must NOT be treated as OAuth. + assert _is_oauth_token("ou1R1z-ft0A-bDeZ9wAA") is False def test_jwt_token(self): # JWTs from OAuth flow