Skip to main content

mcp_kit/auth/
basic.rs

1use std::{future::Future, pin::Pin, sync::Arc};
2
3use crate::{
4    auth::{
5        credentials::Credentials,
6        identity::AuthenticatedIdentity,
7        provider::{AuthFuture, AuthProvider},
8    },
9    error::{McpError, McpResult},
10};
11
12/// A custom async validator for (username, password) pairs.
13pub type BasicValidatorFn = Arc<
14    dyn Fn(String, String) -> Pin<Box<dyn Future<Output = McpResult<AuthenticatedIdentity>> + Send>>
15        + Send
16        + Sync,
17>;
18
19/// Validates `Authorization: Basic <base64(username:password)>` credentials.
20///
21/// The base64 decoding is handled by the transport layer before the credentials
22/// reach this provider; this provider receives the already-decoded username and
23/// password via [`Credentials::Basic`].
24///
25/// # Examples
26///
27/// ```rust,no_run
28/// use mcp_kit::auth::{BasicAuthProvider, AuthenticatedIdentity};
29///
30/// let provider = BasicAuthProvider::with_validator(|username, password| async move {
31///     if username == "admin" && password == "secret" {
32///         Ok(AuthenticatedIdentity::new(username).with_scopes(["admin"]))
33///     } else {
34///         Err(mcp_kit::McpError::Unauthorized("invalid credentials".into()))
35///     }
36/// });
37/// ```
38pub struct BasicAuthProvider {
39    validator: BasicValidatorFn,
40}
41
42impl BasicAuthProvider {
43    /// Create a provider with a custom async validator.
44    pub fn with_validator<F, Fut>(f: F) -> Self
45    where
46        F: Fn(String, String) -> Fut + Send + Sync + 'static,
47        Fut: Future<Output = McpResult<AuthenticatedIdentity>> + Send + 'static,
48    {
49        Self {
50            validator: Arc::new(move |u, p| Box::pin(f(u, p))),
51        }
52    }
53}
54
55impl AuthProvider for BasicAuthProvider {
56    fn authenticate<'a>(&'a self, credentials: &'a Credentials) -> AuthFuture<'a> {
57        Box::pin(async move {
58            match credentials {
59                Credentials::Basic { username, password } => {
60                    (self.validator)(username.clone(), password.clone()).await
61                }
62                _ => Err(McpError::Unauthorized(
63                    "expected basic auth credentials".into(),
64                )),
65            }
66        })
67    }
68
69    fn accepts(&self, credentials: &Credentials) -> bool {
70        matches!(credentials, Credentials::Basic { .. })
71    }
72}