# OAuth Authentication Setup
claude-code-mux now supports OAuth authentication for Claude Pro/Max subscriptions, allowing you to use your Claude subscription without needing an API key!
## Features
- ✅ **Zero Cost**: Max plan users pay $0 for API calls
- ✅ **PKCE Security**: Secure OAuth 2.0 with PKCE (Proof Key for Code Exchange)
- ✅ **Auto Refresh**: Tokens are automatically refreshed when expired
- ✅ **Persistent Storage**: Tokens stored securely in `~/.claude-code-mux/oauth_tokens.json`
## Quick Start
### 1. Get Authorization URL
```rust
use claude_code_mux::auth::{OAuthClient, OAuthConfig, TokenStore};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Initialize
let config = OAuthConfig::anthropic();
let token_store = TokenStore::default()?;
let oauth_client = OAuthClient::new(config, token_store);
// Get authorization URL
let auth_url = oauth_client.get_authorization_url();
println!("Go to: {}", auth_url.url);
println!();
println!("After authorization, you'll receive a code.");
println!("Enter the code here:");
// Read code from user
let mut code = String::new();
std::io::stdin().read_line(&mut code)?;
let code = code.trim();
// Exchange code for tokens
let token = oauth_client.exchange_code(
code,
&auth_url.verifier.verifier,
"anthropic-max" // Provider ID
).await?;
println!("✅ Authentication successful!");
println!("Access token expires at: {}", token.expires_at);
Ok(())
}
```
### 2. Configure Provider
Create `config/default.toml`:
```toml
[server]
host = "127.0.0.1"
port = 13456
[router]
default = "claude-sonnet-4.5"
# OAuth Provider
[[providers]]
name = "claude-max"
provider_type = "anthropic"
auth_type = "oauth" # Use OAuth instead of api_key
oauth_provider = "anthropic-max" # Must match the provider_id used in exchange_code
enabled = true
models = []
[[models]]
name = "claude-sonnet-4.5"
[[models.mappings]]
actual_model = "claude-sonnet-4-5-20250929"
priority = 1
provider = "claude-max"
```
### 3. Start Server
```bash
cargo run -- start
```
## OAuth Configuration Options
### OAuthConfig::anthropic()
For Claude Pro/Max users:
- **Client ID**: `9d1c250a-e61b-44d9-88ed-5944d1962f5e`
- **Auth URL**: `https://claude.ai/oauth/authorize`
- **Token URL**: `https://console.anthropic.com/v1/oauth/token`
- **Scopes**: `org:create_api_key user:profile user:inference`
### OAuthConfig::anthropic_console()
For creating an API key via OAuth (alternative flow):
- Uses console.anthropic.com for authorization
- Creates an API key automatically after OAuth
- Useful if you want a traditional API key workflow
## Token Storage
Tokens are stored in JSON format at `~/.claude-code-mux/oauth_tokens.json`:
```json
{
"anthropic-max": {
"provider_id": "anthropic-max",
"access_token": "ey...",
"refresh_token": "rt_...",
"expires_at": "2025-11-18T15:30:00Z",
"enterprise_url": null
}
}
```
File permissions are automatically set to `0600` (owner read/write only) for security.
## API Endpoints (Future)
The following API endpoints will be added to the admin server:
- `POST /api/oauth/authorize` - Get authorization URL
- `POST /api/oauth/exchange` - Exchange authorization code for tokens
- `GET /api/oauth/tokens` - List all OAuth providers
- `DELETE /api/oauth/tokens/:provider` - Remove OAuth token
## Usage Example
```rust
use claude_code_mux::auth::{OAuthClient, OAuthConfig, TokenStore};
let config = OAuthConfig::anthropic();
let token_store = TokenStore::default()?;
let client = OAuthClient::new(config, token_store);
// Get valid token (auto-refreshes if needed)
let access_token = client.get_valid_token("anthropic-max").await?;
// Use in HTTP request
let response = reqwest::Client::new()
.post("https://api.anthropic.com/v1/messages")
.header("Authorization", format!("Bearer {}", access_token))
.header("anthropic-version", "2023-06-01")
.json(&request_body)
.send()
.await?;
```
## Security Notes
1. **Never commit tokens**: The `oauth_tokens.json` file contains sensitive credentials
2. **File permissions**: Always stored with `0600` permissions (Unix)
3. **PKCE**: Uses SHA-256 challenge for additional security
4. **Auto-refresh**: Tokens are refreshed 5 minutes before expiration
## Comparison: API Key vs OAuth
| Setup | Simple | One-time OAuth flow |
| Cost | Pay per token | $0 (included in subscription) |
| Security| Static key | Rotating tokens + PKCE |
| Sharing | Easy (but unsafe) | Per-user authentication |
| Expiration | Never | Auto-refreshes |
## Troubleshooting
### "Token refresh failed"
- Check your internet connection
- Verify your Max subscription is active
- Re-authenticate: Delete token and run OAuth flow again
### "No token found for provider"
- Run the OAuth authorization flow first
- Check that `oauth_provider` in config matches the provider_id in TokenStore
### "Environment variable not found"
- OAuth doesn't use environment variables
- Make sure `auth_type = "oauth"` is set in provider config
## Related
- [OpenCode Anthropic Auth](https://github.com/sst/opencode-anthropic-auth) - Inspiration for this implementation
- [Anthropic OAuth Docs](https://docs.anthropic.com/claude/reference/oauth)