toolkit_security/authenticator.rs
1//! Transport-agnostic bearer-token authentication abstraction.
2//!
3//! [`BearerAuthenticator`] decouples the HTTP/gRPC transport layers from the
4//! concrete `AuthN` Resolver client. The transport only needs to hand a raw
5//! bearer token to an implementation and receive a reconstructed
6//! [`SecurityContext`] back. The concrete `AuthNResolverClient` adapter is
7//! injected at the gear/bootstrap layer so neither `toolkit-http` nor
8//! `toolkit-transport-grpc` need to depend on the full `ToolKit` framework.
9//!
10//! This lives in `toolkit-security` (not `toolkit-http`) so it stays
11//! transport-agnostic and reusable by the gRPC path — it returns
12//! [`SecurityContext`], which `toolkit-security` already owns, and
13//! `toolkit-security` has no dependency on any transport crate.
14
15use std::future::Future;
16
17use crate::context::SecurityContext;
18
19/// Neutral authentication error returned by a [`BearerAuthenticator`].
20///
21/// Intentionally coarse-grained and transport-agnostic: it never carries the
22/// token or any provider-specific detail so it is safe to surface at a trust
23/// boundary. Concrete adapters map their own error types into these variants.
24#[derive(Debug, thiserror::Error)]
25#[non_exhaustive]
26pub enum AuthNError {
27 /// The token was syntactically present but failed validation
28 /// (invalid signature, expired, malformed claims, etc.).
29 #[error("invalid or expired token")]
30 InvalidToken,
31 /// The authentication backend could not be reached or returned a
32 /// transient failure. Callers may choose to retry or surface a 503.
33 #[error("authentication backend unavailable")]
34 Unavailable,
35 /// Any other authentication failure. The message must not contain the
36 /// token or other sensitive material.
37 #[error("authentication failed: {0}")]
38 Other(String),
39}
40
41/// Re-validates a raw bearer token and reconstructs a [`SecurityContext`].
42///
43/// Implementations perform a full validation on every call — there is no
44/// trusted-peer fast path (zero-trust; see `cpt-cf-adr-two-plane-auth`). The transport layer
45/// stays generic over this trait; the concrete `AuthNResolverClient` adapter
46/// is supplied at the gear/bootstrap layer.
47///
48/// The returned future is `Send` so the trait can be used from Axum/Tower
49/// middleware running on a multi-threaded runtime.
50pub trait BearerAuthenticator: Send + Sync {
51 /// Validate `token` and reconstruct the corresponding [`SecurityContext`].
52 ///
53 /// # Errors
54 ///
55 /// Returns [`AuthNError`] if the token is invalid, the backend is
56 /// unavailable, or authentication otherwise fails.
57 fn authenticate(
58 &self,
59 token: &str,
60 ) -> impl Future<Output = Result<SecurityContext, AuthNError>> + Send;
61}