axum_server_mtls/lib.rs
1//! # axum-server-mtls
2//!
3//! mTLS client certificate extraction for [axum-server](https://crates.io/crates/axum-server).
4//!
5//! `axum-server` does not expose peer certificates after the TLS handshake
6//! ([issue #162](https://github.com/programatik29/axum-server/issues/162)).
7//! This crate fills that gap by wrapping `RustlsAcceptor` with a custom
8//! `Accept` implementation that extracts the client certificate chain and
9//! injects it into every HTTP request as an extension.
10//!
11//! ## Usage
12//!
13//! ```rust,no_run
14//! use axum::{extract::Extension, routing::get, Router};
15//! use axum_server_mtls::{MtlsAcceptor, PeerCertificates};
16//! use axum_server::tls_rustls::{RustlsAcceptor, RustlsConfig};
17//!
18//! #[tokio::main]
19//! async fn main() {
20//! let rustls_config = RustlsConfig::from_pem_file("cert.pem", "key.pem")
21//! .await
22//! .unwrap();
23//!
24//! let app = Router::new().route("/", get(handler));
25//!
26//! let acceptor = MtlsAcceptor::new(RustlsAcceptor::new(rustls_config));
27//!
28//! let addr: std::net::SocketAddr = "0.0.0.0:3000".parse().unwrap();
29//! axum_server::bind(addr)
30//! .acceptor(acceptor)
31//! .serve(app.into_make_service())
32//! .await
33//! .unwrap();
34//! }
35//!
36//! async fn handler(
37//! Extension(certs): Extension<PeerCertificates>,
38//! ) -> String {
39//! match certs.leaf_cn() {
40//! Some(cn) => format!("Hello, {cn}!"),
41//! None => "No client certificate presented.".into(),
42//! }
43//! }
44//! ```
45//!
46//! ## How It Works
47//!
48//! 1. `MtlsAcceptor` delegates the TLS handshake to the inner `RustlsAcceptor`.
49//! 2. After the handshake, it reads `ServerConnection::peer_certificates()`.
50//! 3. It wraps the inner service with a `tower` `Extension` layer so that
51//! every request on that connection carries a [`PeerCertificates`] value.
52//! 4. Handlers extract it via `Extension<PeerCertificates>`.
53//!
54//! The Rustls `ServerConfig` must be built with
55//! [`with_client_cert_verifier`](rustls::ConfigBuilder::with_client_cert_verifier)
56//! (optional or required) for clients to present certificates. If built with
57//! `with_no_client_auth()`, `PeerCertificates` will always be empty.
58
59mod acceptor;
60mod certs;
61
62pub use acceptor::MtlsAcceptor;
63pub use certs::PeerCertificates;