Skip to main content

macp_auth/auth/resolvers/
static_bearer.rs

1use crate::auth::resolver::{AuthError, AuthResolver, ResolvedIdentity};
2use crate::security::AuthIdentity;
3use std::collections::HashMap;
4use tonic::metadata::MetadataMap;
5
6/// Resolves opaque bearer tokens against a pre-loaded identity map.
7/// This is the existing `MACP_AUTH_TOKENS_JSON` mechanism.
8pub struct StaticBearerResolver {
9    identities: HashMap<String, AuthIdentity>,
10}
11
12impl StaticBearerResolver {
13    pub fn new(identities: HashMap<String, AuthIdentity>) -> Self {
14        tracing::info!(
15            count = identities.len(),
16            "static bearer resolver initialized"
17        );
18        Self { identities }
19    }
20
21    fn extract_bearer(metadata: &MetadataMap) -> Option<String> {
22        metadata
23            .get("authorization")
24            .and_then(|v| v.to_str().ok())
25            .and_then(|v| v.strip_prefix("Bearer "))
26            .map(str::to_string)
27            .or_else(|| {
28                metadata
29                    .get("x-macp-token")
30                    .and_then(|v| v.to_str().ok())
31                    .map(str::to_string)
32            })
33    }
34}
35
36#[async_trait::async_trait]
37impl AuthResolver for StaticBearerResolver {
38    fn name(&self) -> &str {
39        "static_bearer"
40    }
41
42    async fn resolve(&self, metadata: &MetadataMap) -> Result<Option<ResolvedIdentity>, AuthError> {
43        let token = match Self::extract_bearer(metadata) {
44            Some(t) => t,
45            None => return Ok(None),
46        };
47
48        // JWT-shaped tokens (contain dots) are not ours — defer to JWT resolver
49        if token.contains('.') {
50            return Ok(None);
51        }
52
53        match self.identities.get(&token) {
54            Some(identity) => Ok(Some(ResolvedIdentity {
55                sender: identity.sender.clone(),
56                allowed_modes: identity.allowed_modes.clone(),
57                can_start_sessions: identity.can_start_sessions,
58                max_open_sessions: identity.max_open_sessions,
59                can_manage_mode_registry: identity.can_manage_mode_registry,
60                is_observer: identity.is_observer,
61                resolver: "static_bearer".to_string(),
62            })),
63            None => Err(AuthError::InvalidCredential(
64                "token not found in identity map".to_string(),
65            )),
66        }
67    }
68}