feat(creds): inject Claude subscription OAuth at the proxy edge#21
Open
trevor-vaughan wants to merge 1 commit into
Open
feat(creds): inject Claude subscription OAuth at the proxy edge#21trevor-vaughan wants to merge 1 commit into
trevor-vaughan wants to merge 1 commit into
Conversation
Let a containerized Claude Code session use a Claude subscription (OAuth) without the real token ever reaching the agent, the same edge-injection model already used for gcloud ADC. - Add AnthropicOAuthInjector: always overrides Authorization: Bearer for api.anthropic.com and .claude.ai from a creds file mounted into the proxy. - Piggyback Claude Code's own OAuth refresh inside the proxy instead of refreshing on a timer: rewrite the agent's refresh request with the real token, capture the rotated tokens upstream, hand the agent a dummy response. - Wire a new "anthropic_oauth" injector type via ANTHROPIC_OAUTH_CREDS_FILE, inert when the var is unset (same as the gcloud entry). - Net effect: the agent only ever holds dummy bearer tokens; the real credential lives solely in the proxy. Details: Injector (internal/credentials/anthropic_oauth.go): - Lazy-loads the creds file (~/.claude/.credentials.json shape) under a mutex; the injector itself never calls the OAuth endpoint. - CurrentRefreshToken exposes the real refresh token to the intercept layer. - UpdateFromRefresh records rotated tokens and writes them back atomically (temp file + rename, 0600), preserving scopes and subscriptionType. Keeps the existing refresh token when the response omits a rotated one. Refresh piggyback (internal/proxy/proxy.go, oauth_refresh.go): - Request hook rewrites only genuine grant_type=refresh_token bodies. An authorization_code grant or invalid JSON passes through untouched and is not marked for response interception, which closes a bug where a non-refresh 200 body got replaced with a dummy. - Response hook captures the rotated tokens (when access_token is non-empty) and swaps in a dummy body that mirrors expires_in so the agent's local expiry stays in sync. Non-200 responses pass through so the agent sees the real failure. Token vending (internal/credentials/token_vending.go): - Add IsAnthropicTokenExchange for console.anthropic.com and platform.claude.com (/v1/oauth/token, /api/oauth/token), matching host with or without the :443 suffix. Correct two misleading comments. Logging (internal/proxy/redact.go): - logRefreshDiag is the single named site for refresh logging. It records host and path only, never token material, bodies, or Authorization headers. Config wiring (config.go, credentials.json, main.go): - BuildFromConfig returns the injector as a 4th value; main threads it into proxy.Config.AnthropicInjector to activate the refresh hooks in production. - Default routing table gains the ANTHROPIC_OAUTH_CREDS_FILE entry pointing at api.anthropic.com and .claude.ai. Tests: - Cover injection/override, rotation with field preservation, the refresh-only-on-refresh-grant contract, a guard that no token string ever reaches the log, config wiring (non-nil with creds, nil without), and an end-to-end refresh integration. Security note: the injected token is scoped to Anthropic endpoints and is meant to serve Claude Code over this transport only, not for independent token use. Assisted-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Let a containerized Claude Code session use a Claude subscription (OAuth) without the real token ever reaching the agent, the same edge-injection model already used for gcloud ADC.
Details:
Injector (internal/credentials/anthropic_oauth.go):
Refresh piggyback (internal/proxy/proxy.go, oauth_refresh.go):
Token vending (internal/credentials/token_vending.go):
Logging (internal/proxy/redact.go):
Config wiring (config.go, credentials.json, main.go):
Tests:
Security note: the injected token is scoped to Anthropic endpoints and is meant to serve Claude Code over this transport only, not for independent token use.
Assisted-By: Claude Opus 4.8 (1M context) noreply@anthropic.com