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
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#[derive(Clone, Default)]
47pub enum Auth {
48 ApiKey(String),
50 #[default]
52 FromEnv,
53 #[cfg(feature = "cli-integration")]
56 ClaudeCli,
57 OAuth { token: String },
59 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 pub fn resolved(credential: Credential) -> Self {
114 Self::Resolved(credential)
115 }
116
117 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 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 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}