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