1mod 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#[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 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 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}