Skip to main content

claude_utils/mcp/
auth.rs

1use rand::Rng;
2use std::fs;
3use std::path::PathBuf;
4use std::sync::Arc;
5use tokio::sync::RwLock;
6
7use crate::Result;
8
9#[derive(Debug, Clone)]
10pub struct AuthConfig {
11    pub token_path: PathBuf,
12    pub require_auth: bool,
13}
14
15impl Default for AuthConfig {
16    fn default() -> Self {
17        let token_path = dirs::home_dir()
18            .unwrap_or_else(|| PathBuf::from("."))
19            .join(".claude-utils")
20            .join("auth.token");
21
22        Self {
23            token_path,
24            require_auth: true,
25        }
26    }
27}
28
29pub struct AuthManager {
30    config: AuthConfig,
31    token: Arc<RwLock<Option<String>>>,
32}
33
34impl AuthManager {
35    pub async fn new(config: AuthConfig) -> Result<Self> {
36        let manager = Self {
37            config,
38            token: Arc::new(RwLock::new(None)),
39        };
40
41        manager.initialize().await?;
42        Ok(manager)
43    }
44
45    async fn initialize(&self) -> Result<()> {
46        if !self.config.require_auth {
47            return Ok(());
48        }
49
50        // Ensure directory exists
51        if let Some(parent) = self.config.token_path.parent() {
52            fs::create_dir_all(parent)?;
53        }
54
55        // Load or generate token
56        let token = if self.config.token_path.exists() {
57            fs::read_to_string(&self.config.token_path)?
58                .trim()
59                .to_string()
60        } else {
61            let new_token = self.generate_token();
62            self.save_token(&new_token)?;
63            new_token
64        };
65
66        *self.token.write().await = Some(token);
67        Ok(())
68    }
69
70    fn generate_token(&self) -> String {
71        let mut rng = rand::thread_rng();
72        let token_bytes: Vec<u8> = (0..32).map(|_| rng.gen()).collect();
73        hex::encode(token_bytes)
74    }
75
76    fn save_token(&self, token: &str) -> Result<()> {
77        fs::write(&self.config.token_path, token)?;
78
79        // Set restrictive permissions on Unix
80        #[cfg(unix)]
81        {
82            use std::os::unix::fs::PermissionsExt;
83            let permissions = fs::Permissions::from_mode(0o600);
84            fs::set_permissions(&self.config.token_path, permissions)?;
85        }
86
87        Ok(())
88    }
89
90    pub async fn validate_token(&self, provided_token: Option<&str>) -> bool {
91        if !self.config.require_auth {
92            return true;
93        }
94
95        let stored_token = self.token.read().await;
96        match (&*stored_token, provided_token) {
97            (Some(stored), Some(provided)) => stored == provided,
98            _ => false,
99        }
100    }
101
102    pub async fn get_token(&self) -> Option<String> {
103        self.token.read().await.clone()
104    }
105}
106
107// Hex encoding utility
108mod hex {
109    pub fn encode(bytes: Vec<u8>) -> String {
110        bytes.iter().map(|b| format!("{b:02x}")).collect::<String>()
111    }
112}