symfonic.core.oauth_provider¶
oauth_provider ¶
AnthropicOAuthProvider — DEV/TEST USE ONLY.
ToS caveat¶
Replaying a Claude subscription OAuth token to the Anthropic API from a
third-party program is subscription-auth reuse, which may be throttled or
disallowed under your plan's Terms of Service. Anthropic ships the
sanctioned claude -p CLI for subscription-backed use. Treat this
provider as the same risk class as
:class:~symfonic.core.codex_provider.CodexOAuthProvider — a local
dev/test convenience only. For production, prefer
:class:~symfonic.core.providers.AnthropicProvider with an
ANTHROPIC_API_KEY.
Uses Claude Code's OAuth access token to call Anthropic's messages API against the user's Claude Pro/Max subscription instead of an API key.
When to use¶
- Local development without provisioning a separate ANTHROPIC_API_KEY
- Running the scaffolded test suite without API credits
- Experimenting with the full pipeline (autonomous learning, metacognition) against a real LLM during development
When NOT to use¶
- ❌ Production multi-tenant SaaS — subscription tokens don't bill per-tenant, rate limits are different from API-key plans, and token refresh requires an interactive OAuth flow that breaks headless deployments.
- ❌ CI that runs across many developers — each dev's token bills against their own subscription.
- ❌ Cost-tracking tests — subscription usage doesn't map cleanly to
the per-token pricing in
symfonic.core.observability.pricing.
This provider explicitly refuses to work in environments that look like
production (SYMFONIC_ENV=production or ENVIRONMENT=production)
to prevent accidental use under multi-tenant billing.
Credential sources¶
- macOS keychain:
security find-generic-password -s "Claude Code-credentials" -w - Linux/Windows:
~/.claude/.credentials.json
Both return a JSON document shaped like::
{
"claudeAiOauth": {
"accessToken": "sk-ant-oat01-…",
"refreshToken": "sk-ant-ort01-…",
"expiresAt": 1776462035348,
"scopes": ["user:inference", …],
"subscriptionType": "max" | "pro" | …
}
}
AnthropicOAuthProvider ¶
AnthropicOAuthProvider(
*,
allow_production: bool = False,
access_token: str | None = None,
refresh_token: str | None = None,
credentials_resolver: Callable[[], Mapping[str, str]]
| None = None,
)
DEV/TEST provider that uses Claude Code's OAuth token.
Instantiate via AnthropicOAuthProvider() on a developer machine
that has run claude login. Pass the instance to
SymfonicAgent(model_provider=...).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
allow_production
|
bool
|
Skip the production-env guard. Default False. Set True only if you have an explicit, ToS-reviewed reason. |
False
|
access_token
|
str | None
|
SECONDARY single-tenant convenience (v8.5.0,
issue #4). A static OAuth access token bound at construction.
Documented footgun: if this provider instance is cached
and shared across tenants, the static token is reused by
every request — re-introducing the cross-tenant leak. Use
|
None
|
refresh_token
|
str | None
|
Optional static refresh token companion to
|
None
|
credentials_resolver
|
Callable[[], Mapping[str, str]] | None
|
PRIMARY multi-tenant path (v8.5.0). A
zero-arg callable invoked inside :meth: |
None
|
Credential precedence (inside :meth:get_chat_model):
credentials_resolver() > static access_token > eager
Keychain / credentials.json load (the v8.4.1 fallback).
Raises:
| Type | Description |
|---|---|
OAuthCredentialError
|
Credentials missing or malformed on the eager-load fallback path. |
ProductionMisuseError
|
Detected a production env var and
|
Source code in src/symfonic/core/oauth_provider.py
credential_source
property
¶
Return where the credential was loaded from.
"keychain" on macOS (Keychain item "Claude Code-credentials"),
an absolute filesystem path on the fallback path, or
"resolver" / "static" when an explicit seam is wired.
token_fingerprint
property
¶
Return a short identifier for the active token.
Useful for logging / banners so you can tell whether two
processes are sharing the same Claude Code session. Format:
sk-ant-oat01-…xxxxxxxx (prefix + last 8 chars).
On the explicit-seam paths the token is request-scoped and not
held on the instance, so we fingerprint the static
access_token when present (resolver-supplied tokens are never
retained, hence <empty>).
get_chat_model ¶
Return a ChatAnthropic using the OAuth token.
Strategy: construct ChatAnthropic with a placeholder api_key,
then swap its internal _client / _async_client for
anthropic SDK clients built with auth_token=... (which
selects Bearer auth at the SDK level) + the required
anthropic-beta: oauth-2025-04-20 default header.
Subclassing is avoided so this provider keeps behaving like a
drop-in replacement for AnthropicProvider.
Source code in src/symfonic/core/oauth_provider.py
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | |
OAuthCredentialError ¶
Bases: RuntimeError
Raised when Claude Code credentials cannot be loaded.
ProductionMisuseError ¶
Bases: RuntimeError
Raised when AnthropicOAuthProvider is used in a production-like env.