use crate::config::Authorization;
use tracing::{debug, trace, warn};
pub struct SecurityObserver<'a> {
authorization: &'a Authorization,
}
impl<'a> SecurityObserver<'a> {
pub fn new(authorization: &'a Authorization) -> Self {
Self { authorization }
}
pub fn observe_request(&self, operation_id: &str, has_auth: bool, requires_auth: bool) {
match self.authorization {
Authorization::None if has_auth => {
debug!(
operation_id,
"Authorization header stripped (MCP-compliant mode)"
);
}
#[cfg(feature = "authorization-token-passthrough")]
Authorization::PassthroughWarn(_) if has_auth => {
debug!(
operation_id,
"Forwarding Authorization header (passthrough mode)"
);
}
#[cfg(feature = "authorization-token-passthrough")]
Authorization::PassthroughSilent(_) => {
trace!(operation_id, has_auth, "Processing request");
}
_ => {
trace!(operation_id, has_auth, requires_auth, "Processing request");
}
}
if requires_auth && !has_auth {
warn!(
operation_id,
"OpenAPI spec requires auth but no Authorization header present"
);
}
}
pub fn log_startup(&self) {
match self.authorization {
Authorization::None => {
tracing::info!("Authorization mode: compliant (headers will not be forwarded)");
}
#[cfg(feature = "authorization-token-passthrough")]
Authorization::PassthroughWarn(_) => {
tracing::warn!(
"Authorization mode: passthrough (non-MCP-compliant) - \
Authorization headers WILL be forwarded to backend APIs. See SECURITY.md"
);
}
#[cfg(feature = "authorization-token-passthrough")]
Authorization::PassthroughSilent(_) => {
tracing::info!("Authorization mode: passthrough-silent");
}
}
}
}