feat(auth): add Claude subscription sign-in (claude/<model>, no API key) - #1333
Draft
calvinchengx wants to merge 1 commit into
Draft
calvinchengx wants to merge 1 commit into
calvinchengx wants to merge 1 commit into
Conversation
Run Strix on a Claude Pro/Max subscription with STRIX_LLM="claude/<model>" and no API key, mirroring the existing ChatGPT (Codex) subscription path. - strix/config/claude.py: Anthropic OAuth 2.0 + PKCE, shared token store, cross-process refresh guard, per-request token via oauth_api_key(). - strix/config/subscription.py: provider-agnostic facade so telemetry, cost reporting, and env validation treat both subscriptions uniformly. - models.py routes claude/<slug> through LiteLLM's anthropic provider, which detects the sk-ant-oat token prefix and sends it as an OAuth bearer with the oauth beta header; the token is refreshed on every request. - strix auth login/status/logout, environment validation, and the expired -token hint are provider-aware. claude/ = subscription; anthropic/ = key. The Anthropic OAuth constants are reverse-engineered from Claude Code and, like the Codex path, using a subscription outside the vendor's own apps is not officially supported. Needs a live sign-in to verify the handshake.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a Claude Pro/Max subscription sign-in, the direct analogue of the existing
strix auth login chatgpt(Codex) path — OAuth 2.0 + PKCE against Anthropic, tokens stored in~/.strix/subscription-auth.json,claude/<model>as a STRIX_LLM.Important
Read this before reviewing. The OAuth authentication here works end-to-end, but the path cannot currently perform inference without impersonating Claude Code — which this PR deliberately does not do. See "Root-cause finding" below. I'm opening it as a draft for maintainer discussion, not as a merge-ready usable path.
How it works
strix/config/claude.py— Anthropic OAuth 2.0 + PKCE, a token store shared with the Codex path (keyed per provider), a cross-process refresh guard, andoauth_api_key().strix/config/subscription.py— a provider-agnostic facade so telemetry, cost reporting, and env validation treat both subscriptions uniformly.strix/config/models.py) —claude/<slug>builds a LiteLLManthropic/<slug>model. LiteLLM detects thesk-ant-oat…token prefix and switches toAuthorization: Bearer+ theoauth-2025-04-20beta header, so no custom translation is needed; a thin subclass re-reads the token per request so long runs survive expiry.strix auth login|status|logoutare provider-aware (chatgpt/claude, withcodex/anthropicaliases); env validation and the expired-token hint recognise both.What is verified working
sk-ant-oat…, refreshsk-ant-ort…, account email).Testing against the live backend, every inference call from Strix returned
429 rate_limit_error(message"Error", no rate-limit headers). That looks like throttling, but it is not. Two byte-identical requests, differing only in one field:429 rate_limit_error,"Error", no headers"You are Claude Code, Anthropic's official CLI for Claude."allowed(~28%/52% used)So the account has ample quota; Anthropic simply only serves inference on a Claude Code OAuth token when the request presents itself as Claude Code (the exact system-prompt identity string). The misleading
429is a disguised access-control rejection of non-Claude-Code callers.Consequence for this PR: the
claude/route sends Strix's own pentesting system prompts, not the Claude Code identity, so it authenticates but is refused inference. Making it work would require injecting the Claude Code system prompt on every request — i.e. impersonating Claude Code to defeat an Anthropic anti-abuse control, and corrupting Strix's own agent identity in the process. This PR intentionally does not do that.Why it's still opened
The OAuth plumbing (login/exchange/refresh/env-wiring) is correct, tested, and mirrors the accepted Codex design — so it's a sound base if the project decides how it wants to handle Anthropic subscriptions. But as-is, a
claude/model cannot run a scan; a meteredanthropic/API key remains the working path. Maintainers should decide whether to (a) keep this as auth-only scaffolding, (b) close it, or (c) take a documented position on the identity requirement — given the ToS considerations, I did not want to make that call by shipping the impersonation.Tests
tests/test_claude_auth.py— PKCE, authorize URL, JSON token exchange (incl. state), refresh + rotation, store round-trip, subscription-model routing, and the shared facade.tests/test_auth_cli.py— provider resolution/aliases, Claude login flow, manual-only callback, per-provider logout.tests/test_models.py—claude/…routes to a LiteLLManthropic/…model and refreshes the bearer per request.ruff,ruff format,mypy,banditclean; full suite passes (one pre-existing, unrelatedtest_pricing.pygrok-routing failure tied to the installed LiteLLM data).