| Crates.io | anthropic-auth |
| lib.rs | anthropic-auth |
| version | 0.1.1 |
| created_at | 2025-12-31 06:11:09.484214+00 |
| updated_at | 2026-01-04 07:52:49.467244+00 |
| description | Anthropic/Claude OAuth 2.0 authentication with PKCE - sync and async APIs |
| homepage | |
| repository | https://github.com/querymt/anthropic-auth |
| max_upload_size | |
| id | 2013980 |
| size | 117,720 |
A Rust library for Anthropic/Claude OAuth 2.0 authentication with PKCE support.
Provides both synchronous (blocking) and asynchronous (runtime-agnostic) APIs for authenticating with Anthropic's OAuth 2.0 endpoints.
[dependencies]
anthropic-auth = "0.1"
use anthropic_auth::{OAuthClient, OAuthConfig, OAuthMode};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = OAuthClient::new(OAuthConfig::default())?;
let flow = client.start_flow(OAuthMode::Max)?;
println!("Visit: {}", flow.authorization_url);
// User visits URL and gets response in format: "code#state"
let response = "l0pnTslNFOmTgp28REMrbt4wyLNR25SJePqjk4CAHjoen0TJ#FgE6g_6khGKFFhXAw3tULPM00CPaqgE3Cq6id79Surg";
// Library automatically parses and validates the state
let tokens = client.exchange_code(response, &flow.state, &flow.verifier)?;
println!("Got access token!");
// Later, refresh if needed
if tokens.is_expired() {
let new_tokens = client.refresh_token(&tokens.refresh_token)?;
}
Ok(())
}
use anthropic_auth::{OAuthClient, OAuthConfig, OAuthMode};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = OAuthClient::new(OAuthConfig::default())?;
let flow = client.start_flow(OAuthMode::Console)?;
println!("Visit: {}", flow.authorization_url);
// User authorizes and you receive: "code#state"
let response = "code123#state456";
let tokens = client.exchange_code(response, &flow.state, &flow.verifier)?;
// Create API key
let api_key = client.create_api_key(&tokens.access_token)?;
println!("API Key: {}", api_key);
Ok(())
}
use anthropic_auth::{AsyncOAuthClient, OAuthConfig, OAuthMode};
#[tokio::main] // or async-std, smol, etc.
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = AsyncOAuthClient::new(OAuthConfig::default())?;
let flow = client.start_flow(OAuthMode::Max)?;
println!("Visit: {}", flow.authorization_url);
// User authorizes and you receive: "code#state"
let response = "code123#state456";
let tokens = client.exchange_code(response, &flow.state, &flow.verifier).await?;
println!("Got access token!");
Ok(())
}
Use this mode if you have a Claude Pro or Claude Max subscription:
let flow = client.start_flow(OAuthMode::Max)?;
https://claude.ai/oauth/authorizeUse this mode to create API keys programmatically:
let flow = client.start_flow(OAuthMode::Console)?;
// ... get tokens ...
let api_key = client.create_api_key(&tokens.access_token)?;
https://console.anthropic.com/oauth/authorize| Feature | Description | Default |
|---|---|---|
blocking |
Synchronous/blocking API | ✅ Yes |
async |
Asynchronous API (runtime-agnostic) | ❌ No |
browser |
Auto-open browser for authorization | ✅ Yes |
callback-server |
Local server for OAuth callback (requires tokio) | ❌ No |
full |
Enable all features | ❌ No |
[dependencies]
anthropic-auth = { version = "0.1", features = ["async"] }
[dependencies]
anthropic-auth = { version = "0.1", features = ["callback-server"] }
tokio = { version = "1", features = ["full"] }
use anthropic_auth::{OAuthClient, OAuthConfig};
let config = OAuthConfig::builder()
.client_id("my-client-id")
.redirect_port(8080) // Custom port
.build();
let client = OAuthClient::new(config)?;
See the examples/ directory for complete working examples:
max_subscription_sync.rs - Claude Pro/Max OAuth (sync)console_api_key_sync.rs - API key creation (sync)Run examples with:
cargo run --example max_subscription_sync
cargo run --example console_api_key_sync
Anthropic returns authorization responses in the format code#state. The library parses this automatically and validates the state for CSRF protection:
// User receives: "abc123#xyz789"
// Library parses it and validates state matches flow.state
let tokens = client.exchange_code("abc123#xyz789", &flow.state, &flow.verifier)?;
// Or if you've already separated them:
let tokens = client.exchange_code("abc123", &flow.state, &flow.verifier)?;
Important: The state parameter is used for CSRF protection. The library validates that the state returned by Anthropic matches the state originally sent in the authorization URL.
let client = OAuthClient::new(config)?;
// Start flow (generates PKCE and state, returns auth URL)
let flow = client.start_flow(OAuthMode::Max)?;
// Exchange code for tokens (parses "code#state" format automatically)
let tokens = client.exchange_code("code#state", &flow.state, &flow.verifier)?;
// Refresh expired tokens
let new_tokens = client.refresh_token(&tokens.refresh_token)?;
// Create API key (Console mode only)
let api_key = client.create_api_key(&tokens.access_token)?;
let client = AsyncOAuthClient::new(config)?;
// Start flow (still sync - no I/O)
let flow = client.start_flow(OAuthMode::Max)?;
// Async methods
let tokens = client.exchange_code("code#state", &flow.state, &flow.verifier).await?;
let new_tokens = client.refresh_token(&tokens.refresh_token).await?;
let api_key = client.create_api_key(&tokens.access_token).await?;
use anthropic_auth::open_browser;
let flow = client.start_flow(OAuthMode::Max)?;
open_browser(&flow.authorization_url)?; // Opens user's default browser
This library intentionally does not handle token persistence. You should store tokens securely based on your application's needs.
Recommended approaches:
keyring crate