code_mesh_core/auth/
mod.rs1use 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#[async_trait]
19pub trait Auth: Send + Sync {
20 fn provider_id(&self) -> &str;
22
23 async fn get_credentials(&self) -> crate::Result<AuthCredentials>;
25
26 async fn set_credentials(&self, credentials: AuthCredentials) -> crate::Result<()>;
28
29 async fn remove_credentials(&self) -> crate::Result<()>;
31
32 async fn has_credentials(&self) -> bool;
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38#[serde(tag = "type", rename_all = "lowercase")]
39pub enum AuthCredentials {
40 ApiKey {
42 key: String,
43 },
44
45 OAuth {
47 access_token: String,
48 refresh_token: Option<String>,
49 expires_at: Option<u64>,
50 },
51
52 Custom {
54 data: HashMap<String, serde_json::Value>,
55 },
56}
57
58impl AuthCredentials {
59 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 pub fn api_key(key: impl Into<String>) -> Self {
75 Self::ApiKey { key: key.into() }
76 }
77
78 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#[async_trait]
94pub trait AuthStorage: Send + Sync {
95 async fn get(&self, provider_id: &str) -> crate::Result<Option<AuthCredentials>>;
97
98 async fn set(&self, provider_id: &str, credentials: AuthCredentials) -> crate::Result<()>;
100
101 async fn remove(&self, provider_id: &str) -> crate::Result<()>;
103
104 async fn list(&self) -> crate::Result<Vec<String>>;
106}
107
108pub struct FileAuthStorage {
110 path: PathBuf,
111}
112
113impl FileAuthStorage {
114 pub fn new(path: PathBuf) -> Self {
116 Self { path }
117 }
118
119 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 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