turbomcp_auth/providers/
api_key.rs

1//! API Key Authentication Provider
2//!
3//! Simple API key-based authentication for service-to-service communication.
4
5use std::collections::HashMap;
6use std::sync::Arc;
7
8use async_trait::async_trait;
9use tokio::sync::RwLock;
10
11use super::super::config::AuthProviderType;
12use super::super::context::AuthContext;
13use super::super::types::{AuthCredentials, AuthProvider, TokenInfo, UserInfo};
14use turbomcp_protocol::{Error as McpError, Result as McpResult};
15
16/// API Key authentication provider
17#[derive(Debug)]
18pub struct ApiKeyProvider {
19    /// Provider name
20    name: String,
21    /// Valid API keys with associated user info
22    api_keys: Arc<RwLock<HashMap<String, UserInfo>>>,
23}
24
25impl ApiKeyProvider {
26    /// Create a new API key provider
27    #[must_use]
28    pub fn new(name: String) -> Self {
29        Self {
30            name,
31            api_keys: Arc::new(RwLock::new(HashMap::new())),
32        }
33    }
34
35    /// Add an API key
36    pub async fn add_api_key(&self, key: String, user_info: UserInfo) {
37        self.api_keys.write().await.insert(key, user_info);
38    }
39
40    /// Remove an API key
41    pub async fn remove_api_key(&self, key: &str) -> bool {
42        self.api_keys.write().await.remove(key).is_some()
43    }
44
45    /// List all API keys (returns keys only, not full info for security)
46    pub async fn list_api_keys(&self) -> Vec<String> {
47        self.api_keys.read().await.keys().cloned().collect()
48    }
49}
50
51#[async_trait]
52impl AuthProvider for ApiKeyProvider {
53    fn name(&self) -> &str {
54        &self.name
55    }
56
57    fn provider_type(&self) -> AuthProviderType {
58        AuthProviderType::ApiKey
59    }
60
61    async fn authenticate(&self, credentials: AuthCredentials) -> McpResult<AuthContext> {
62        match credentials {
63            AuthCredentials::ApiKey { key } => {
64                let api_keys = self.api_keys.read().await;
65                if let Some(user_info) = api_keys.get(&key) {
66                    let token = TokenInfo {
67                        access_token: key,
68                        token_type: "ApiKey".to_string(),
69                        refresh_token: None,
70                        expires_in: None,
71                        scope: None,
72                    };
73
74                    AuthContext::builder()
75                        .subject(user_info.id.clone())
76                        .user(user_info.clone())
77                        .roles(vec!["api_user".to_string()])
78                        .permissions(vec!["api_access".to_string()])
79                        .request_id(uuid::Uuid::new_v4().to_string())
80                        .token(token)
81                        .provider(self.name.clone())
82                        .build()
83                        .map_err(|e| McpError::internal(e.to_string()))
84                } else {
85                    Err(McpError::internal("Invalid API key".to_string()))
86                }
87            }
88            _ => Err(McpError::internal(
89                "Invalid credentials for API key provider".to_string(),
90            )),
91        }
92    }
93
94    async fn validate_token(&self, token: &str) -> McpResult<AuthContext> {
95        self.authenticate(AuthCredentials::ApiKey {
96            key: token.to_string(),
97        })
98        .await
99    }
100
101    async fn refresh_token(&self, _refresh_token: &str) -> McpResult<TokenInfo> {
102        Err(McpError::internal(
103            "API keys do not support token refresh".to_string(),
104        ))
105    }
106
107    async fn revoke_token(&self, token: &str) -> McpResult<()> {
108        let removed = self.remove_api_key(token).await;
109        if removed {
110            Ok(())
111        } else {
112            Err(McpError::internal("API key not found".to_string()))
113        }
114    }
115
116    async fn get_user_info(&self, token: &str) -> McpResult<UserInfo> {
117        let api_keys = self.api_keys.read().await;
118        api_keys
119            .get(token)
120            .cloned()
121            .ok_or_else(|| McpError::internal("Invalid API key".to_string()))
122    }
123}