github_mcp/auth/strategies/
api_key.rs1use async_trait::async_trait;
4
5use super::super::auth_manager::header_location_for;
6use super::super::auth_strategy::{AuthConfig, AuthStrategy, Credentials};
7use super::super::errors::AuthError;
8use crate::core::config_schema::AuthMethod;
9
10#[derive(Debug, Default)]
11pub struct ApiKeyStrategy;
12
13#[async_trait]
14impl AuthStrategy for ApiKeyStrategy {
15 async fn authenticate(&self, config: &AuthConfig) -> anyhow::Result<Credentials> {
16 let api_key = config
17 .get("api_key")
18 .ok_or_else(|| AuthError::MissingCredentials("api_key".to_string()))?;
19
20 let mut credentials = Credentials::new();
21 credentials.insert("api_key".to_string(), api_key.clone());
22 let (_, header_name) = header_location_for(AuthMethod::ApiKey);
28 credentials.insert("request_header_name".to_string(), header_name.to_string());
29 Ok(credentials)
30 }
31
32 fn validate_credentials(&self, credentials: &Credentials) -> bool {
33 credentials
34 .get("api_key")
35 .is_some_and(|key| !key.is_empty())
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[tokio::test]
44 async fn carries_the_configured_api_key_through() {
45 let strategy = ApiKeyStrategy;
46 let config = AuthConfig::from([("api_key".to_string(), "abc123".to_string())]);
47 let credentials = strategy.authenticate(&config).await.unwrap();
48 assert_eq!(credentials.get("api_key").unwrap(), "abc123");
49 }
50
51 #[tokio::test]
52 async fn rejects_a_config_missing_the_api_key() {
53 let strategy = ApiKeyStrategy;
54 assert!(strategy.authenticate(&AuthConfig::new()).await.is_err());
55 }
56
57 #[test]
58 fn rejects_an_empty_api_key_as_invalid() {
59 let strategy = ApiKeyStrategy;
60 let credentials = Credentials::from([("api_key".to_string(), String::new())]);
61 assert!(!strategy.validate_credentials(&credentials));
62 }
63}