Skip to main content

turul_a2a/middleware/
traits.rs

1//! A2aMiddleware trait and SecurityContribution.
2
3use async_trait::async_trait;
4
5use super::context::RequestContext;
6use super::error::MiddlewareError;
7
8/// Trait that auth middleware implements.
9///
10/// Runs at the Tower layer before any handler or JSON-RPC dispatch.
11#[async_trait]
12pub trait A2aMiddleware: Send + Sync {
13    /// Validate the request and populate identity on the context.
14    async fn before_request(&self, ctx: &mut RequestContext) -> Result<(), MiddlewareError>;
15
16    /// Security contribution for AgentCard auto-population.
17    fn security_contribution(&self) -> SecurityContribution {
18        SecurityContribution::default()
19    }
20}
21
22/// What a middleware contributes to AgentCard security metadata.
23///
24/// Contains both scheme definitions and requirement groups.
25/// Multiple `SecurityRequirement` entries = OR (alternatives).
26/// Multiple schemes in one `SecurityRequirement` = AND (all required).
27#[derive(Debug, Clone, Default)]
28pub struct SecurityContribution {
29    pub schemes: Vec<(String, turul_a2a_proto::SecurityScheme)>,
30    pub requirements: Vec<turul_a2a_proto::SecurityRequirement>,
31}
32
33impl SecurityContribution {
34    pub fn new() -> Self {
35        Self::default()
36    }
37
38    /// Add a scheme with required scopes and a corresponding requirement.
39    pub fn with_scheme(
40        mut self,
41        name: impl Into<String>,
42        scheme: turul_a2a_proto::SecurityScheme,
43        scopes: Vec<String>,
44    ) -> Self {
45        let name = name.into();
46        self.schemes.push((name.clone(), scheme));
47        let mut req_schemes = std::collections::HashMap::new();
48        req_schemes.insert(name, turul_a2a_proto::StringList { list: scopes });
49        self.requirements
50            .push(turul_a2a_proto::SecurityRequirement {
51                schemes: req_schemes,
52            });
53        self
54    }
55
56    pub fn is_empty(&self) -> bool {
57        self.schemes.is_empty() && self.requirements.is_empty()
58    }
59}