Skip to main content

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