Skip to main content

arcp_runtime/auth/
bearer.rs

1//! `bearer` authentication scheme (RFC §8.2).
2
3use std::collections::HashMap;
4
5use async_trait::async_trait;
6
7use arcp_core::auth::{AuthOutcome, Authenticator};
8use arcp_core::error::ARCPError;
9use arcp_core::messages::{AuthScheme, Capabilities, ClientIdentity, Credentials};
10
11/// Trivial in-memory bearer-token store mapping `token -> principal`.
12///
13/// Suitable for tests, examples, and small deployments. Real deployments
14/// should plug their own [`Authenticator`] in front of an external trust
15/// store.
16#[derive(Debug, Default)]
17pub struct BearerAuthenticator {
18    tokens: HashMap<String, String>,
19}
20
21impl BearerAuthenticator {
22    /// Construct an empty authenticator.
23    #[must_use]
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    /// Add a `token -> principal` mapping. Returns `self` for chaining.
29    #[must_use]
30    pub fn with_token(mut self, token: impl Into<String>, principal: impl Into<String>) -> Self {
31        self.tokens.insert(token.into(), principal.into());
32        self
33    }
34}
35
36#[async_trait]
37impl Authenticator for BearerAuthenticator {
38    fn scheme(&self) -> AuthScheme {
39        AuthScheme::Bearer
40    }
41
42    async fn authenticate(
43        &self,
44        creds: &Credentials,
45        _client: &ClientIdentity,
46        _negotiated: &Capabilities,
47    ) -> Result<AuthOutcome, ARCPError> {
48        let Some(token) = &creds.token else {
49            return Ok(AuthOutcome::Reject {
50                reason: "bearer scheme requires a token".into(),
51            });
52        };
53        Ok(self.tokens.get(token).map_or_else(
54            || AuthOutcome::Reject {
55                reason: "unknown bearer token".into(),
56            },
57            |principal| AuthOutcome::Accept {
58                principal: principal.clone(),
59            },
60        ))
61    }
62}