Skip to main content

claude_agent/auth/providers/
environment.rs

1//! Environment variable credential provider.
2
3use async_trait::async_trait;
4
5use crate::auth::{Credential, CredentialProvider};
6use crate::{Error, Result};
7
8const DEFAULT_ENV_VAR: &str = "ANTHROPIC_API_KEY";
9
10/// Provider that reads API key from environment variable.
11pub struct EnvironmentProvider {
12    env_var: String,
13}
14
15impl EnvironmentProvider {
16    /// Create provider using default ANTHROPIC_API_KEY.
17    pub fn new() -> Self {
18        Self {
19            env_var: DEFAULT_ENV_VAR.to_string(),
20        }
21    }
22
23    /// Create provider with custom environment variable.
24    pub fn from_var(env_var: impl Into<String>) -> Self {
25        Self {
26            env_var: env_var.into(),
27        }
28    }
29}
30
31impl Default for EnvironmentProvider {
32    fn default() -> Self {
33        Self::new()
34    }
35}
36
37#[async_trait]
38impl CredentialProvider for EnvironmentProvider {
39    fn name(&self) -> &str {
40        "environment"
41    }
42
43    async fn resolve(&self) -> Result<Credential> {
44        std::env::var(&self.env_var)
45            .map(Credential::api_key)
46            .map_err(|_| Error::auth(format!("{} not set", self.env_var)))
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use secrecy::ExposeSecret;
54
55    #[tokio::test]
56    async fn test_environment_provider_missing() {
57        // SAFETY: Test-only environment setup, single-threaded test context
58        unsafe { std::env::remove_var("TEST_API_KEY_NOT_SET") };
59        let provider = EnvironmentProvider::from_var("TEST_API_KEY_NOT_SET");
60        assert!(provider.resolve().await.is_err());
61    }
62
63    #[tokio::test]
64    async fn test_environment_provider_set() {
65        // SAFETY: Test-only environment setup, single-threaded test context
66        unsafe { std::env::set_var("TEST_API_KEY_SET", "test-key") };
67        let provider = EnvironmentProvider::from_var("TEST_API_KEY_SET");
68        let cred = provider.resolve().await.unwrap();
69        assert!(matches!(&cred, Credential::ApiKey(k) if k.expose_secret() == "test-key"));
70        unsafe { std::env::remove_var("TEST_API_KEY_SET") };
71    }
72}