From ffd5d37f9b50febb2a85343a2052fec08950f199 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Mon, 30 Mar 2026 17:41:13 -0700 Subject: [PATCH] fix: treat non-sk-ant- keys as regular API keys, not OAuth tokens (#4093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: treat non-sk-ant- prefixed keys (Azure AI Foundry) as regular API keys, not OAuth tokens * fix: treat non-sk-ant- keys as regular API keys, not OAuth tokens _is_oauth_token() returned True for any key not starting with sk-ant-api, misclassifying Azure AI Foundry keys as OAuth tokens and sending Bearer auth instead of x-api-key → 401 rejection. Real Anthropic OAuth tokens all start with sk-ant-oat (confirmed from live .credentials.json). Non-sk-ant- keys are third-party provider keys that should use x-api-key. Test fixtures updated to use realistic sk-ant-oat01- prefixed tokens instead of fake strings. Salvaged from PR #4075 by @HangGlidersRule. --------- Co-authored-by: Clawdbot --- agent/anthropic_adapter.py | 6 +++++- tests/agent/test_auxiliary_client.py | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/agent/anthropic_adapter.py b/agent/anthropic_adapter.py index a81736496..74539cbc2 100644 --- a/agent/anthropic_adapter.py +++ b/agent/anthropic_adapter.py @@ -152,13 +152,17 @@ def _is_oauth_token(key: str) -> bool: Regular API keys start with 'sk-ant-api'. Everything else (setup-tokens starting with 'sk-ant-oat', managed keys, JWTs, etc.) needs Bearer auth. + Azure AI Foundry keys (non sk-ant- prefixed) should use x-api-key, not Bearer. """ if not key: return False # Regular Console API keys use x-api-key header if key.startswith("sk-ant-api"): return False - # Everything else (setup-tokens, managed keys, JWTs) uses Bearer auth + # Azure AI Foundry keys don't start with sk-ant- at all — treat as regular API key + if not key.startswith("sk-ant-"): + return False + # Everything else (setup-tokens sk-ant-oat, managed keys, JWTs) uses Bearer auth return True diff --git a/tests/agent/test_auxiliary_client.py b/tests/agent/test_auxiliary_client.py index 35dcee7ad..28ef57289 100644 --- a/tests/agent/test_auxiliary_client.py +++ b/tests/agent/test_auxiliary_client.py @@ -310,7 +310,7 @@ class TestExpiredCodexFallback: def test_hermes_oauth_file_sets_oauth_flag(self, monkeypatch): """OAuth-style tokens should get is_oauth=True (token is not sk-ant-api-*).""" # Mock resolve_anthropic_token to return an OAuth-style token - with patch("agent.anthropic_adapter.resolve_anthropic_token", return_value="hermes-oauth-jwt-token"), \ + with patch("agent.anthropic_adapter.resolve_anthropic_token", return_value="sk-ant-oat01-hermes-oauth-test"), \ patch("agent.anthropic_adapter.build_anthropic_client") as mock_build: mock_build.return_value = MagicMock() from agent.auxiliary_client import _try_anthropic, AnthropicAuxiliaryClient @@ -364,7 +364,7 @@ class TestExpiredCodexFallback: def test_claude_code_oauth_env_sets_flag(self, monkeypatch): """CLAUDE_CODE_OAUTH_TOKEN env var should get is_oauth=True.""" - monkeypatch.setenv("CLAUDE_CODE_OAUTH_TOKEN", "cc-oauth-token-test") + monkeypatch.setenv("CLAUDE_CODE_OAUTH_TOKEN", "sk-ant-oat01-cc-oauth-test") monkeypatch.delenv("ANTHROPIC_TOKEN", raising=False) with patch("agent.anthropic_adapter.build_anthropic_client") as mock_build: mock_build.return_value = MagicMock()