mod cache;
mod config;
mod credential;
mod helper;
mod provider;
mod providers;
#[cfg(feature = "cli-integration")]
mod storage;
use std::sync::Arc;
use secrecy::{ExposeSecret, SecretString};
pub use cache::CachedProvider;
pub use config::{CLAUDE_CODE_BETA, OAuthConfig, OAuthConfigBuilder};
pub use credential::{Credential, OAuthCredential};
pub use helper::{ApiKeyHelper, AwsCredentialRefresh, AwsCredentials, CredentialManager};
pub use provider::CredentialProvider;
#[cfg(feature = "cli-integration")]
pub use providers::ClaudeCliProvider;
pub use providers::{ChainProvider, EnvironmentProvider, ExplicitProvider};
#[cfg(feature = "cli-integration")]
pub use storage::CliCredentials;
use crate::Result;
#[derive(Clone, Default)]
pub enum Auth {
ApiKey(SecretString),
#[default]
FromEnv,
#[cfg(feature = "cli-integration")]
ClaudeCli,
OAuth {
token: SecretString,
},
Resolved(Credential),
#[cfg(feature = "aws")]
Bedrock {
region: String,
},
#[cfg(feature = "gcp")]
Vertex {
project: String,
region: String,
},
#[cfg(feature = "azure")]
Foundry {
resource: String,
},
}
impl Auth {
pub fn api_key(key: impl Into<String>) -> Self {
Self::ApiKey(SecretString::from(key.into()))
}
pub fn from_env() -> Self {
Self::FromEnv
}
#[cfg(feature = "cli-integration")]
pub fn claude_cli() -> Self {
Self::ClaudeCli
}
pub fn oauth(token: impl Into<String>) -> Self {
Self::OAuth {
token: SecretString::from(token.into()),
}
}
#[cfg(feature = "aws")]
pub fn bedrock(region: impl Into<String>) -> Self {
Self::Bedrock {
region: region.into(),
}
}
#[cfg(feature = "gcp")]
pub fn vertex(project: impl Into<String>, region: impl Into<String>) -> Self {
Self::Vertex {
project: project.into(),
region: region.into(),
}
}
#[cfg(feature = "azure")]
pub fn foundry(resource: impl Into<String>) -> Self {
Self::Foundry {
resource: resource.into(),
}
}
pub fn resolved(credential: Credential) -> Self {
Self::Resolved(credential)
}
pub async fn resolve(&self) -> Result<Credential> {
match self {
Self::ApiKey(key) => Ok(Credential::api_key(key.expose_secret())),
Self::FromEnv => EnvironmentProvider::new().resolve().await,
#[cfg(feature = "cli-integration")]
Self::ClaudeCli => ClaudeCliProvider::new().resolve().await,
Self::OAuth { token } => Ok(Credential::oauth(token.expose_secret())),
Self::Resolved(credential) => Ok(credential.clone()),
#[cfg(feature = "aws")]
Self::Bedrock { .. } => Ok(Credential::placeholder()),
#[cfg(feature = "gcp")]
Self::Vertex { .. } => Ok(Credential::placeholder()),
#[cfg(feature = "azure")]
Self::Foundry { .. } => Ok(Credential::placeholder()),
}
}
pub async fn resolve_with_provider(
&self,
) -> Result<(Credential, Option<Arc<dyn CredentialProvider>>)> {
match self {
Self::ApiKey(key) => Ok((Credential::api_key(key.expose_secret()), None)),
Self::FromEnv => {
let provider = EnvironmentProvider::new();
let credential = provider.resolve().await?;
Ok((credential, None))
}
#[cfg(feature = "cli-integration")]
Self::ClaudeCli => {
let provider = Arc::new(ClaudeCliProvider::new());
let credential = provider.resolve().await?;
Ok((credential, Some(provider)))
}
Self::OAuth { token } => Ok((Credential::oauth(token.expose_secret()), None)),
Self::Resolved(credential) => Ok((credential.clone(), None)),
#[cfg(feature = "aws")]
Self::Bedrock { .. } => Ok((Credential::placeholder(), None)),
#[cfg(feature = "gcp")]
Self::Vertex { .. } => Ok((Credential::placeholder(), None)),
#[cfg(feature = "azure")]
Self::Foundry { .. } => Ok((Credential::placeholder(), None)),
}
}
pub fn is_cloud_provider(&self) -> bool {
#[allow(unreachable_patterns)]
match self {
#[cfg(feature = "aws")]
Self::Bedrock { .. } => true,
#[cfg(feature = "gcp")]
Self::Vertex { .. } => true,
#[cfg(feature = "azure")]
Self::Foundry { .. } => true,
_ => false,
}
}
pub fn is_oauth(&self) -> bool {
match self {
Self::OAuth { .. } => true,
#[cfg(feature = "cli-integration")]
Self::ClaudeCli => true,
Self::Resolved(cred) => cred.is_oauth(),
_ => false,
}
}
pub fn supports_server_tools(&self) -> bool {
!self.is_cloud_provider()
}
}
impl From<&str> for Auth {
fn from(key: &str) -> Self {
Self::ApiKey(SecretString::from(key))
}
}
impl From<String> for Auth {
fn from(key: String) -> Self {
Self::ApiKey(SecretString::from(key))
}
}
impl From<Credential> for Auth {
fn from(credential: Credential) -> Self {
Self::Resolved(credential)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auth_from_str() {
let auth: Auth = "sk-test-key".into();
assert!(matches!(auth, Auth::ApiKey(_)));
}
#[test]
fn test_auth_from_string() {
let auth: Auth = "sk-test-key".to_string().into();
assert!(matches!(auth, Auth::ApiKey(_)));
}
#[test]
fn test_auth_default() {
let auth = Auth::default();
assert!(matches!(auth, Auth::FromEnv));
}
#[test]
fn test_auth_constructors() {
assert!(matches!(Auth::api_key("key"), Auth::ApiKey(_)));
assert!(matches!(Auth::from_env(), Auth::FromEnv));
#[cfg(feature = "cli-integration")]
assert!(matches!(Auth::claude_cli(), Auth::ClaudeCli));
assert!(matches!(Auth::oauth("token"), Auth::OAuth { .. }));
assert!(matches!(
Auth::resolved(Credential::api_key("key")),
Auth::Resolved(_)
));
}
#[test]
fn test_auth_from_credential() {
let cred = Credential::api_key("test-key");
let auth: Auth = cred.into();
assert!(matches!(auth, Auth::Resolved(_)));
}
#[tokio::test]
async fn test_auth_resolved_resolve() {
let cred = Credential::api_key("test-key");
let auth = Auth::resolved(cred);
let resolved = auth.resolve().await.unwrap();
assert!(!resolved.is_placeholder());
}
}