code_mesh_core/auth/
mod.rs

1//! Authentication system for Code Mesh
2
3use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::path::PathBuf;
7
8pub mod anthropic;
9pub mod storage;
10pub mod github_copilot;
11pub mod manager;
12
13pub use anthropic::AnthropicAuth;
14pub use github_copilot::{GitHubCopilotAuth, GitHubCopilotAuthResult};
15pub use manager::AuthManager;
16
17/// Authentication trait for provider credentials
18#[async_trait]
19pub trait Auth: Send + Sync {
20    /// Get the provider ID this auth is for
21    fn provider_id(&self) -> &str;
22    
23    /// Get current valid credentials
24    async fn get_credentials(&self) -> crate::Result<AuthCredentials>;
25    
26    /// Store new credentials
27    async fn set_credentials(&self, credentials: AuthCredentials) -> crate::Result<()>;
28    
29    /// Remove stored credentials
30    async fn remove_credentials(&self) -> crate::Result<()>;
31    
32    /// Check if credentials are available
33    async fn has_credentials(&self) -> bool;
34}
35
36/// Authentication credentials
37#[derive(Debug, Clone, Serialize, Deserialize)]
38#[serde(tag = "type", rename_all = "lowercase")]
39pub enum AuthCredentials {
40    /// API key authentication
41    ApiKey {
42        key: String,
43    },
44    
45    /// OAuth authentication
46    OAuth {
47        access_token: String,
48        refresh_token: Option<String>,
49        expires_at: Option<u64>,
50    },
51    
52    /// Custom authentication
53    Custom {
54        data: HashMap<String, serde_json::Value>,
55    },
56}
57
58impl AuthCredentials {
59    /// Check if credentials are expired
60    pub fn is_expired(&self) -> bool {
61        match self {
62            AuthCredentials::OAuth { expires_at: Some(exp), .. } => {
63                let now = std::time::SystemTime::now()
64                    .duration_since(std::time::UNIX_EPOCH)
65                    .unwrap()
66                    .as_secs();
67                now >= *exp
68            }
69            _ => false,
70        }
71    }
72    
73    /// Create API key credentials
74    pub fn api_key(key: impl Into<String>) -> Self {
75        Self::ApiKey { key: key.into() }
76    }
77    
78    /// Create OAuth credentials
79    pub fn oauth(
80        access_token: impl Into<String>,
81        refresh_token: Option<impl Into<String>>,
82        expires_at: Option<u64>,
83    ) -> Self {
84        Self::OAuth {
85            access_token: access_token.into(),
86            refresh_token: refresh_token.map(|t| t.into()),
87            expires_at,
88        }
89    }
90}
91
92/// Authentication storage trait
93#[async_trait]
94pub trait AuthStorage: Send + Sync {
95    /// Get credentials for a provider
96    async fn get(&self, provider_id: &str) -> crate::Result<Option<AuthCredentials>>;
97    
98    /// Set credentials for a provider
99    async fn set(&self, provider_id: &str, credentials: AuthCredentials) -> crate::Result<()>;
100    
101    /// Remove credentials for a provider
102    async fn remove(&self, provider_id: &str) -> crate::Result<()>;
103    
104    /// List all stored provider IDs
105    async fn list(&self) -> crate::Result<Vec<String>>;
106}
107
108/// File-based authentication storage
109pub struct FileAuthStorage {
110    path: PathBuf,
111}
112
113impl FileAuthStorage {
114    /// Create a new file-based auth storage
115    pub fn new(path: PathBuf) -> Self {
116        Self { path }
117    }
118    
119    /// Create default file-based auth storage with error handling
120    pub fn default_with_result() -> crate::Result<Self> {
121        let home_dir = dirs::home_dir()
122            .ok_or_else(|| crate::Error::Storage(crate::storage::StorageError::Other("Could not determine home directory".to_string())))?;
123        let path = home_dir.join(".code-mesh").join("auth.json");
124        Ok(Self::new(path))
125    }
126}
127
128impl Default for FileAuthStorage {
129    fn default() -> Self {
130        // Use a default path in the user's home directory
131        let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
132        let path = home_dir.join(".code-mesh").join("auth.json");
133        Self::new(path)
134    }
135}
136