Rebase onto upstream (a4d95fd)
#12
@@ -0,0 +1,46 @@
|
||||
"""OAuth utility functions for Anthropic subscription auth."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
def is_oauth_token(token: str | None) -> bool:
|
||||
"""Check if token is an OAuth token (vs regular API key).
|
||||
|
||||
OAuth tokens from Claude Max/Pro contain 'sk-ant-oat' prefix.
|
||||
Regular API keys use 'sk-ant-api03' or similar.
|
||||
"""
|
||||
if not token:
|
||||
return False
|
||||
return "sk-ant-oat" in token
|
||||
|
||||
|
||||
def get_auth_headers(token: str, is_oauth: bool = False) -> dict[str, str]:
|
||||
"""Get authentication headers for Anthropic API.
|
||||
|
||||
OAuth tokens require Authorization: Bearer header.
|
||||
Regular API keys use x-api-key header.
|
||||
"""
|
||||
headers: dict[str, str] = {
|
||||
"anthropic-version": "2023-06-01",
|
||||
"content-type": "application/json",
|
||||
}
|
||||
|
||||
if is_oauth:
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
# Required headers to mimic Claude Code client
|
||||
headers["anthropic-beta"] = "claude-code-20250219,oauth-2025-04-20"
|
||||
headers["anthropic-dangerous-direct-browser-access"] = "true"
|
||||
headers["user-agent"] = "claude-cli/2.1.2 (external, cli)"
|
||||
headers["x-app"] = "cli"
|
||||
else:
|
||||
headers["x-api-key"] = token
|
||||
|
||||
return headers
|
||||
|
||||
|
||||
def get_claude_code_system_prefix() -> str:
|
||||
"""Get the required system prompt prefix for OAuth tokens.
|
||||
|
||||
Anthropic requires this identity declaration for OAuth auth.
|
||||
"""
|
||||
return "You are Claude Code, Anthropic's official CLI for Claude."
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Test OAuth utility functions."""
|
||||
import pytest
|
||||
from nanobot.providers.oauth_utils import is_oauth_token, get_auth_headers
|
||||
|
||||
|
||||
def test_is_oauth_token_detects_oat():
|
||||
"""Should detect sk-ant-oat tokens as OAuth."""
|
||||
assert is_oauth_token("sk-ant-oat01-buSdhCH2XEkebW7ZQZTvGqH5EwAFh4u52LrdJhAP") is True
|
||||
assert is_oauth_token("sk-ant-api03-regularkey") is False
|
||||
assert is_oauth_token("") is False
|
||||
assert is_oauth_token(None) is False
|
||||
|
||||
|
||||
def test_get_auth_headers_oauth():
|
||||
"""OAuth tokens should use Authorization: Bearer."""
|
||||
headers = get_auth_headers("sk-ant-oat01-xxx", is_oauth=True)
|
||||
assert "Authorization" in headers
|
||||
assert headers["Authorization"] == "Bearer sk-ant-oat01-xxx"
|
||||
assert "x-api-key" not in headers
|
||||
assert headers["anthropic-beta"] == "claude-code-20250219,oauth-2025-04-20"
|
||||
|
||||
|
||||
def test_get_auth_headers_api_key():
|
||||
"""Regular API keys should use x-api-key."""
|
||||
headers = get_auth_headers("sk-ant-api03-xxx", is_oauth=False)
|
||||
assert "x-api-key" in headers
|
||||
assert headers["x-api-key"] == "sk-ant-api03-xxx"
|
||||
assert "Authorization" not in headers
|
||||
Reference in New Issue
Block a user