claude_agent/auth/
mod.rs

1//! Authentication module for Claude API.
2//!
3//! The `Auth` enum is the primary entry point for all authentication methods.
4//! SDK users should use `Auth` to configure authentication, which internally
5//! resolves to `Credential` for API requests.
6
7mod cache;
8mod config;
9mod credential;
10mod helper;
11mod provider;
12mod providers;
13#[cfg(feature = "cli-integration")]
14mod storage;
15
16use std::sync::Arc;
17
18pub use cache::CachedProvider;
19pub use config::{CLAUDE_CODE_BETA, OAuthConfig, OAuthConfigBuilder};
20pub use credential::{Credential, OAuthCredential};
21pub use helper::{ApiKeyHelper, AwsCredentialRefresh, AwsCredentials, CredentialManager};
22pub use provider::CredentialProvider;
23#[cfg(feature = "cli-integration")]
24pub use providers::ClaudeCliProvider;
25pub use providers::{ChainProvider, EnvironmentProvider, ExplicitProvider};
26#[cfg(feature = "cli-integration")]
27pub use storage::CliCredentials;
28
29use crate::Result;
30
31/// Primary authentication configuration for SDK usage.
32///
33/// `Auth` provides a unified interface for all authentication methods.
34/// Use this enum to configure how the SDK authenticates with Claude API.
35///
36/// # Variants
37///
38/// - `ApiKey`: Direct API key authentication
39/// - `FromEnv`: Load API key from ANTHROPIC_API_KEY environment variable
40/// - `ClaudeCli`: Use credentials from Claude Code CLI (requires `cli-integration` feature)
41/// - `OAuth`: OAuth token authentication
42/// - `Resolved`: Pre-resolved credential (for testing or credential reuse)
43/// - `Bedrock`: AWS Bedrock (requires `aws` feature)
44/// - `Vertex`: GCP Vertex AI (requires `gcp` feature)
45/// - `Foundry`: Azure Foundry (requires `azure` feature)
46#[derive(Clone, Default)]
47pub enum Auth {
48    /// Direct API key authentication.
49    ApiKey(String),
50    /// Load API key from ANTHROPIC_API_KEY environment variable.
51    #[default]
52    FromEnv,
53    /// Use credentials from Claude Code CLI (~/.claude/credentials.json).
54    /// Requires `cli-integration` feature.
55    #[cfg(feature = "cli-integration")]
56    ClaudeCli,
57    /// OAuth token authentication.
58    OAuth { token: String },
59    /// Use a pre-resolved credential directly.
60    /// Useful for testing, credential reuse, or custom credential sources.
61    Resolved(Credential),
62    #[cfg(feature = "aws")]
63    Bedrock { region: String },
64    #[cfg(feature = "gcp")]
65    Vertex { project: String, region: String },
66    #[cfg(feature = "azure")]
67    Foundry { resource: String },
68}
69
70impl Auth {
71    pub fn api_key(key: impl Into<String>) -> Self {
72        Self::ApiKey(key.into())
73    }
74
75    pub fn from_env() -> Self {
76        Self::FromEnv
77    }
78
79    #[cfg(feature = "cli-integration")]
80    pub fn claude_cli() -> Self {
81        Self::ClaudeCli
82    }
83
84    pub fn oauth(token: impl Into<String>) -> Self {
85        Self::OAuth {
86            token: token.into(),
87        }
88    }
89
90    #[cfg(feature = "aws")]
91    pub fn bedrock(region: impl Into<String>) -> Self {
92        Self::Bedrock {
93            region: region.into(),
94        }
95    }
96
97    #[cfg(feature = "gcp")]
98    pub fn vertex(project: impl Into<String>, region: impl Into<String>) -> Self {
99        Self::Vertex {
100            project: project.into(),
101            region: region.into(),
102        }
103    }
104
105    #[cfg(feature = "azure")]
106    pub fn foundry(resource: impl Into<String>) -> Self {
107        Self::Foundry {
108            resource: resource.into(),
109        }
110    }
111
112    /// Use a pre-resolved credential directly.
113    pub fn resolved(credential: Credential) -> Self {
114        Self::Resolved(credential)
115    }
116
117    /// Resolve authentication to internal credential format.
118    pub async fn resolve(&self) -> Result<Credential> {
119        match self {
120            Self::ApiKey(key) => Ok(Credential::api_key(key)),
121            Self::FromEnv => EnvironmentProvider::new().resolve().await,
122            #[cfg(feature = "cli-integration")]
123            Self::ClaudeCli => ClaudeCliProvider::new().resolve().await,
124            Self::OAuth { token } => Ok(Credential::oauth(token)),
125            Self::Resolved(credential) => Ok(credential.clone()),
126            #[cfg(feature = "aws")]
127            Self::Bedrock { .. } => Ok(Credential::default()),
128            #[cfg(feature = "gcp")]
129            Self::Vertex { .. } => Ok(Credential::default()),
130            #[cfg(feature = "azure")]
131            Self::Foundry { .. } => Ok(Credential::default()),
132        }
133    }
134
135    /// Resolve authentication and return both credential and provider.
136    ///
137    /// Returns the credential provider for auth methods that support token refresh.
138    /// This enables automatic 401 retry with credential refresh.
139    pub async fn resolve_with_provider(
140        &self,
141    ) -> Result<(Credential, Option<Arc<dyn CredentialProvider>>)> {
142        match self {
143            Self::ApiKey(key) => Ok((Credential::api_key(key), None)),
144            Self::FromEnv => {
145                let provider = EnvironmentProvider::new();
146                let credential = provider.resolve().await?;
147                Ok((credential, None))
148            }
149            #[cfg(feature = "cli-integration")]
150            Self::ClaudeCli => {
151                let provider = Arc::new(ClaudeCliProvider::new());
152                let credential = provider.resolve().await?;
153                Ok((credential, Some(provider)))
154            }
155            Self::OAuth { token } => Ok((Credential::oauth(token), None)),
156            Self::Resolved(credential) => Ok((credential.clone(), None)),
157            #[cfg(feature = "aws")]
158            Self::Bedrock { .. } => Ok((Credential::default(), None)),
159            #[cfg(feature = "gcp")]
160            Self::Vertex { .. } => Ok((Credential::default(), None)),
161            #[cfg(feature = "azure")]
162            Self::Foundry { .. } => Ok((Credential::default(), None)),
163        }
164    }
165
166    pub fn is_cloud_provider(&self) -> bool {
167        #[allow(unreachable_patterns)]
168        match self {
169            #[cfg(feature = "aws")]
170            Self::Bedrock { .. } => true,
171            #[cfg(feature = "gcp")]
172            Self::Vertex { .. } => true,
173            #[cfg(feature = "azure")]
174            Self::Foundry { .. } => true,
175            _ => false,
176        }
177    }
178
179    pub fn is_oauth(&self) -> bool {
180        match self {
181            Self::OAuth { .. } => true,
182            #[cfg(feature = "cli-integration")]
183            Self::ClaudeCli => true,
184            Self::Resolved(cred) => cred.is_oauth(),
185            _ => false,
186        }
187    }
188
189    /// Check if Anthropic's server-side tools (WebSearch, WebFetch) are available.
190    ///
191    /// Server-side tools are available with Anthropic direct API (API Key or OAuth)
192    /// but NOT with cloud providers (Bedrock, Vertex, Foundry).
193    pub fn supports_server_tools(&self) -> bool {
194        !self.is_cloud_provider()
195    }
196}
197
198impl From<&str> for Auth {
199    fn from(key: &str) -> Self {
200        Self::ApiKey(key.to_string())
201    }
202}
203
204impl From<String> for Auth {
205    fn from(key: String) -> Self {
206        Self::ApiKey(key)
207    }
208}
209
210impl From<Credential> for Auth {
211    fn from(credential: Credential) -> Self {
212        Self::Resolved(credential)
213    }
214}
215
216#[cfg(test)]
217mod tests {
218    use super::*;
219
220    #[test]
221    fn test_auth_from_str() {
222        let auth: Auth = "sk-test-key".into();
223        assert!(matches!(auth, Auth::ApiKey(_)));
224    }
225
226    #[test]
227    fn test_auth_from_string() {
228        let auth: Auth = "sk-test-key".to_string().into();
229        assert!(matches!(auth, Auth::ApiKey(_)));
230    }
231
232    #[test]
233    fn test_auth_default() {
234        let auth = Auth::default();
235        assert!(matches!(auth, Auth::FromEnv));
236    }
237
238    #[test]
239    fn test_auth_constructors() {
240        assert!(matches!(Auth::api_key("key"), Auth::ApiKey(_)));
241        assert!(matches!(Auth::from_env(), Auth::FromEnv));
242        #[cfg(feature = "cli-integration")]
243        assert!(matches!(Auth::claude_cli(), Auth::ClaudeCli));
244        assert!(matches!(Auth::oauth("token"), Auth::OAuth { .. }));
245        assert!(matches!(
246            Auth::resolved(Credential::api_key("key")),
247            Auth::Resolved(_)
248        ));
249    }
250
251    #[test]
252    fn test_auth_from_credential() {
253        let cred = Credential::api_key("test-key");
254        let auth: Auth = cred.into();
255        assert!(matches!(auth, Auth::Resolved(_)));
256    }
257
258    #[tokio::test]
259    async fn test_auth_resolved_resolve() {
260        let cred = Credential::api_key("test-key");
261        let auth = Auth::resolved(cred);
262        let resolved = auth.resolve().await.unwrap();
263        assert!(!resolved.is_default());
264    }
265}