use crate::error::FastMCPError;
use crate::mcp::types::JsonRpcRequest;
use crate::server::auth::{AuthContext, AuthProvider};
use async_trait::async_trait;
use reqwest;
use serde::Deserialize;
pub struct GitHubProvider {
user_agent: String,
}
#[derive(Deserialize, Debug)]
pub struct GitHubUser {
pub id: u64,
pub login: String,
pub email: Option<String>,
}
impl GitHubProvider {
pub fn new() -> Self {
Self {
user_agent: "rs-fast-mcp-server".to_string(),
}
}
}
impl Default for GitHubProvider {
fn default() -> Self {
Self::new()
}
}
impl GitHubProvider {
pub fn with_user_agent(agent: &str) -> Self {
Self {
user_agent: agent.to_string(),
}
}
}
#[async_trait]
impl AuthProvider for GitHubProvider {
async fn verify(&self, request: &JsonRpcRequest) -> Result<AuthContext, FastMCPError> {
let token = request
.transport_metadata
.as_ref()
.and_then(|metadata| {
metadata
.get("authorization")
.or_else(|| metadata.get("Authorization"))
})
.and_then(|h| h.strip_prefix("Bearer "))
.or_else(|| {
request
.params
.as_ref()
.and_then(|p| p.get("token"))
.and_then(|v| v.as_str())
})
.ok_or_else(|| FastMCPError::InvalidRequest("Missing token parameter".to_string()))?;
let client = reqwest::Client::new();
let resp = client
.get("https://api.github.com/user")
.header("Authorization", format!("Bearer {}", token))
.header("User-Agent", &self.user_agent)
.header("Accept", "application/vnd.github.v3+json")
.send()
.await
.map_err(|e| FastMCPError::new(format!("GitHub validation request failed: {}", e)))?;
if !resp.status().is_success() {
return Err(FastMCPError::InvalidRequest(
"Invalid GitHub Token".to_string(),
));
}
let user: GitHubUser = resp
.json()
.await
.map_err(|e| FastMCPError::new(format!("Failed to parse GitHub user info: {}", e)))?;
Ok(AuthContext {
client_id: Some(user.login.clone()),
user_id: Some(user.id.to_string()),
scopes: vec![], })
}
}