Skip to main content

github_mcp/auth/strategies/
basic.rs

1// GitHub v3 REST API MCP server — generated by mcpify. Do not hand-edit.
2
3use async_trait::async_trait;
4use base64::Engine;
5use base64::engine::general_purpose::STANDARD;
6
7use super::super::auth_strategy::{AuthConfig, AuthStrategy, Credentials};
8use super::super::errors::AuthError;
9
10#[derive(Debug, Default)]
11pub struct BasicAuthStrategy;
12
13#[async_trait]
14impl AuthStrategy for BasicAuthStrategy {
15    async fn authenticate(&self, config: &AuthConfig) -> anyhow::Result<Credentials> {
16        let username = config
17            .get("username")
18            .ok_or_else(|| AuthError::MissingCredentials("username, password".to_string()))?;
19        let password = config
20            .get("password")
21            .ok_or_else(|| AuthError::MissingCredentials("username, password".to_string()))?;
22
23        let token = STANDARD.encode(format!("{username}:{password}"));
24        let mut credentials = Credentials::new();
25        credentials.insert("username".to_string(), username.clone());
26        credentials.insert("password".to_string(), password.clone());
27        credentials.insert("authorization_header".to_string(), format!("Basic {token}"));
28        Ok(credentials)
29    }
30
31    fn validate_credentials(&self, credentials: &Credentials) -> bool {
32        credentials.contains_key("authorization_header")
33    }
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    fn config(username: &str, password: &str) -> AuthConfig {
41        AuthConfig::from([
42            ("username".to_string(), username.to_string()),
43            ("password".to_string(), password.to_string()),
44        ])
45    }
46
47    #[tokio::test]
48    async fn encodes_username_and_password_as_a_basic_authorization_header() {
49        let strategy = BasicAuthStrategy;
50        let credentials = strategy
51            .authenticate(&config("alice", "s3cr3t"))
52            .await
53            .unwrap();
54        assert_eq!(
55            credentials.get("authorization_header").unwrap(),
56            "Basic YWxpY2U6czNjcjN0"
57        );
58    }
59
60    #[tokio::test]
61    async fn rejects_a_config_missing_either_field() {
62        let strategy = BasicAuthStrategy;
63        assert!(strategy.authenticate(&AuthConfig::new()).await.is_err());
64    }
65
66    #[test]
67    fn validates_credentials_carrying_an_authorization_header() {
68        let strategy = BasicAuthStrategy;
69        let credentials =
70            Credentials::from([("authorization_header".to_string(), "Basic abc".to_string())]);
71        assert!(strategy.validate_credentials(&credentials));
72        assert!(!strategy.validate_credentials(&Credentials::new()));
73    }
74}