The Claude Code SDK supports two authentication methods:
- OAuth Authentication (recommended for Claude Code Max plan users)
- API Key Authentication (traditional method)
Claude Code Max plan users can authenticate using OAuth, eliminating the need to manage API keys.
# Install the SDK
pip install claude-code-sdk
# Login with OAuth
claude-auth loginThis will:
- Open your browser for authentication
- Save the authentication token locally
- Allow SDK usage without API keys
import asyncio
from claude_code_sdk import query_with_oauth
async def main():
# Uses OAuth authentication automatically
async for message in query_with_oauth(prompt="Hello Claude!"):
print(message)
asyncio.run(main())The SDK implements the OAuth 2.0 Authorization Code flow:
- Authorization: Opens browser to Claude Console for user login
- Callback: Local server receives authorization code
- Token Exchange: Code is exchanged for access token
- Token Storage: Tokens are securely stored locally
- Auto-refresh: Expired tokens are automatically refreshed
# Login with OAuth
claude-auth login
# Check authentication status
claude-auth status
# Refresh expired token
claude-auth refresh
# Logout and clear tokens
claude-auth logoutfrom claude_code_sdk import query_with_oauth
async for message in query_with_oauth(prompt="Your prompt"):
print(message)from claude_code_sdk import login, logout
# Perform OAuth login
await login()
# Clear authentication
await logout()from claude_code_sdk import OAuthConfig, authenticated_query
config = OAuthConfig(
client_id="your-client-id",
authorize_url="https://custom-auth-url",
token_url="https://custom-token-url",
)
async for message in authenticated_query(
prompt="Hello",
oauth_config=config
):
print(message)Tokens are stored in ~/.claude_code/tokens.json with restricted permissions (0600).
from claude_code_sdk import ClaudeAuth
async with ClaudeAuth(use_oauth=True) as auth:
# Check if authenticated
token = auth.token_storage.load_token()
if token:
print(f"Authenticated: {not token.is_expired()}")
print(f"Expires at: {token.expires_at}")Configure OAuth via environment variables:
CLAUDE_OAUTH_CLIENT_ID: OAuth client IDCLAUDE_OAUTH_CLIENT_SECRET: OAuth client secret (if required)CLAUDE_OAUTH_REDIRECT_URI: OAuth redirect URI (default: http://localhost:8089/callback)CLAUDE_OAUTH_AUTHORIZE_URL: Authorization endpointCLAUDE_OAUTH_TOKEN_URL: Token endpoint
Traditional API key authentication is still supported.
export ANTHROPIC_API_KEY="your-api-key"from claude_code_sdk import query
async for message in query(prompt="Hello"):
print(message)from claude_code_sdk import query_with_api_key
async for message in query_with_api_key(
prompt="Hello",
api_key="your-api-key"
):
print(message)The authenticated_query function supports both methods:
from claude_code_sdk import authenticated_query
# Try OAuth first, fall back to API key
async for message in authenticated_query(
prompt="Hello",
use_oauth=True, # Try OAuth
api_key="fallback-key" # Fallback to API key
):
print(message)-
OAuth Tokens:
- Stored with restricted permissions (0600)
- Never commit token files to version control
- Tokens expire and auto-refresh
-
API Keys:
- Use environment variables
- Never hardcode in source code
- Rotate keys regularly
-
Custom OAuth Apps:
- Keep client secrets secure
- Use HTTPS for redirect URIs in production
- Implement proper CSRF protection with state parameter
-
Browser doesn't open:
# Manually visit the URL shown in terminal -
Token expired:
claude-auth refresh # Or just use the SDK - it auto-refreshes -
Permission denied:
# Fix token file permissions chmod 600 ~/.claude_code/tokens.json
-
No API key found:
export ANTHROPIC_API_KEY="your-key"
-
Invalid API key:
- Check key validity in Anthropic Console
- Ensure no extra spaces in key
-
Install latest SDK:
pip install -U claude-code-sdk
-
Login with OAuth:
claude-auth login
-
Update code:
# Old from claude_code_sdk import query # New (OAuth) from claude_code_sdk import query_with_oauth
The original query() function still works with API keys:
# Still works with ANTHROPIC_API_KEY
from claude_code_sdk import query
async for message in query(prompt="Hello"):
print(message)from claude_code_sdk import (
AuthenticationMiddleware,
query_with_middleware,
get_auth_headers
)
# Create custom auth middleware
auth_middleware = AuthenticationMiddleware(
auth_headers_provider=get_auth_headers
)
async for message in query_with_middleware(
prompt="Hello",
middleware=[auth_middleware]
):
print(message)from claude_code_sdk import TokenStorage, ClaudeAuth
# Use different storage paths for different accounts
personal_storage = TokenStorage(Path.home() / ".claude_code" / "personal.json")
work_storage = TokenStorage(Path.home() / ".claude_code" / "work.json")
# Switch between accounts
async with ClaudeAuth(token_storage=personal_storage) as auth:
# Use personal account
passlogin(): Perform OAuth loginlogout(): Clear stored tokensget_auth_headers(): Get current auth headersquery_with_oauth(): Query using OAuthquery_with_api_key(): Query using API keyauthenticated_query(): Query with flexible auth
ClaudeAuth: Main authentication handlerOAuthConfig: OAuth configurationOAuthFlow: OAuth flow implementationAuthToken: Authentication tokenTokenStorage: Token persistenceAuthenticationError: Auth-specific errors
claude-auth login: OAuth loginclaude-auth logout: Clear tokensclaude-auth status: Check auth statusclaude-auth refresh: Refresh token