http-signature-normalization-actix 0.11.3

An HTTP Signatures library that leaves the signing to you
Documentation
//! Types for verifying requests with Actix Web

use crate::{Config, PrepareVerifyError, SignatureVerify, Spawn};
use actix_web::{
    body::MessageBody,
    dev::{Payload, Service, ServiceRequest, ServiceResponse, Transform},
    http::StatusCode,
    Error, FromRequest, HttpMessage, HttpRequest, HttpResponse, ResponseError,
};
use futures_core::future::LocalBoxFuture;
use std::{
    collections::HashSet,
    future::{ready, Ready},
    rc::Rc,
    task::{Context, Poll},
};
use tracing::{debug, Span};
use tracing_error::SpanTrace;
use tracing_futures::Instrument;

#[derive(Clone, Debug)]
/// A marker type that is used to guard routes
pub struct SignatureVerified(String);

impl SignatureVerified {
    /// Return the Key ID used to verify the request
    ///
    /// It might be important for an application to verify that the payload being processed indeed
    /// belongs to the owner of the key used to sign the request.
    pub fn key_id(&self) -> &str {
        &self.0
    }
}

#[derive(Clone, Debug)]
/// The Verify signature middleware
///
/// ```rust,ignore
/// let middleware = VerifySignature::new(MyVerifier::new(), Config::default()).authorization();
///
/// HttpServer::new(move || {
///     App::new()
///         .wrap(middleware.clone())
///         .route("/protected", web::post().to(|_: SignatureVerified| "Verified Authorization Header"))
///         .route("/unprotected", web::post().to(|| "No verification required"))
/// })
/// ```
pub struct VerifySignature<T, Spawner>(T, Config<Spawner>, HeaderKind);

#[derive(Debug)]
#[doc(hidden)]
pub struct VerifyMiddleware<T, Spawner, S>(Rc<S>, Config<Spawner>, HeaderKind, T);

#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
enum HeaderKind {
    Authorization,
    Signature,
}

impl std::fmt::Display for HeaderKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Authorization => {
                write!(f, "Authorization")
            }
            Self::Signature => {
                write!(f, "Signature")
            }
        }
    }
}

#[derive(Clone)]
#[doc(hidden)]
pub struct VerifyError {
    context: String,
    kind: VerifyErrorKind,
}

impl std::fmt::Debug for VerifyError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "{:?}", self.kind)
    }
}

impl std::fmt::Display for VerifyError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "{}", self.kind)?;
        std::fmt::Display::fmt(&self.context, f)
    }
}

impl std::error::Error for VerifyError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.kind.source()
    }
}

#[derive(Clone, Debug)]
enum VerifyErrorKind {
    MissingSignature,
    ExpiredSignature(String),
    ParseField(&'static str),
    ParseSignature,
    Extension,
    MissingHeader(HashSet<String>),
}

impl std::fmt::Display for VerifyErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::MissingSignature => f.write_str("Signature or Authorization header is missing"),
            Self::ExpiredSignature(expired) => write!(f, "Signature is expired, {expired}"),
            Self::ParseField(field) => write!(f, "Signature field {field} could not be parsed"),
            Self::ParseSignature => f.write_str("Signature is not a valid string"),
            Self::Extension => f.write_str("Request extension not present"),
            Self::MissingHeader(headers) => {
                let missing_headers = headers
                    .iter()
                    .map(String::as_str)
                    .collect::<Vec<_>>()
                    .join(", ");
                write!(f, "Required headers are missing: {missing_headers}")
            }
        }
    }
}
impl std::error::Error for VerifyErrorKind {}

impl VerifyError {
    fn new(span: &Span, kind: VerifyErrorKind) -> Self {
        span.in_scope(|| VerifyError {
            context: SpanTrace::capture().to_string(),
            kind,
        })
    }
}

impl<T, Spawner> VerifySignature<T, Spawner>
where
    T: SignatureVerify,
{
    /// Create a new middleware for verifying HTTP Signatures. A type implementing
    /// [`SignatureVerify`] is required, as well as a Config
    ///
    /// By default, this middleware expects to verify Signature headers, and requires the presence
    /// of the header
    pub fn new(verify_signature: T, config: Config<Spawner>) -> Self
    where
        Spawner: Spawn,
    {
        VerifySignature(verify_signature, config, HeaderKind::Signature)
    }

    /// Verify Authorization headers instead of Signature headers
    pub fn authorization(self) -> Self {
        VerifySignature(self.0, self.1, HeaderKind::Authorization)
    }
}

impl<T, Spawner, S, B> VerifyMiddleware<T, Spawner, S>
where
    T: SignatureVerify + Clone + 'static,
    T::Future: 'static,
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    B: MessageBody + 'static,
{
    fn handle(
        &self,
        span: Span,
        req: ServiceRequest,
    ) -> LocalBoxFuture<'static, Result<ServiceResponse<B>, Error>> {
        let mut headers = req.headers().clone();
        if !headers.contains_key("host") {
            if let Some(host) = req.head().uri.host().map(|host| {
                if let Some(port) = req.head().uri.port() {
                    format!("{host}:{port}")
                } else {
                    host.to_string()
                }
            }) {
                if let Ok(host) = actix_http::header::HeaderValue::try_from(host) {
                    headers.insert(actix_http::header::HeaderName::from_static("host"), host);
                }
            }
        }

        let res = self
            .1
            .begin_verify(req.method(), req.uri().path_and_query(), headers);

        let unverified = match res {
            Ok(unverified) => unverified,
            Err(PrepareVerifyError::Expired(reason)) => {
                return Box::pin(ready(Err(VerifyError::new(
                    &span,
                    VerifyErrorKind::ExpiredSignature(reason),
                )
                .into())));
            }
            Err(PrepareVerifyError::Missing) => {
                return Box::pin(ready(Err(VerifyError::new(
                    &span,
                    VerifyErrorKind::MissingSignature,
                )
                .into())));
            }
            Err(PrepareVerifyError::ParseField(field)) => {
                return Box::pin(ready(Err(VerifyError::new(
                    &span,
                    VerifyErrorKind::ParseField(field),
                )
                .into())));
            }
            Err(PrepareVerifyError::Header(_)) => {
                return Box::pin(ready(Err(VerifyError::new(
                    &span,
                    VerifyErrorKind::ParseSignature,
                )
                .into())));
            }
            Err(PrepareVerifyError::Required(mut req)) => {
                return Box::pin(ready(Err(VerifyError::new(
                    &span,
                    VerifyErrorKind::MissingHeader(req.take_headers()),
                )
                .into())));
            }
        };

        let algorithm = unverified.algorithm().cloned();
        let key_id = unverified.key_id().to_owned();

        let verify_fut = unverified.verify(|signature, signing_string| {
            span.in_scope(|| {
                self.3.clone().signature_verify(
                    algorithm,
                    key_id.clone(),
                    signature.to_string(),
                    signing_string.to_string(),
                )
            })
        });

        let service = Rc::clone(&self.0);

        Box::pin(async move {
            if verify_fut.instrument(span).await? {
                req.extensions_mut().insert(SignatureVerified(key_id));
            }

            service.call(req).await
        })
    }
}

impl HeaderKind {
    pub fn is_authorization(self) -> bool {
        HeaderKind::Authorization == self
    }

    pub fn is_signature(self) -> bool {
        HeaderKind::Signature == self
    }
}

impl FromRequest for SignatureVerified {
    type Error = VerifyError;
    type Future = Ready<Result<Self, Self::Error>>;

    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
        let res = req
            .extensions()
            .get::<Self>()
            .cloned()
            .ok_or_else(|| VerifyError::new(&Span::current(), VerifyErrorKind::Extension));

        if res.is_err() {
            debug!("Failed to fetch SignatureVerified from request");
        }

        ready(res)
    }
}

impl<T, Spawner, S, B> Transform<S, ServiceRequest> for VerifySignature<T, Spawner>
where
    T: SignatureVerify + Clone + 'static,
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error> + 'static,
    S::Error: 'static,
    B: MessageBody + 'static,
    Spawner: Clone,
{
    type Response = ServiceResponse<B>;
    type Error = actix_web::Error;
    type Transform = VerifyMiddleware<T, Spawner, S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(VerifyMiddleware(
            Rc::new(service),
            self.1.clone(),
            self.2,
            self.0.clone(),
        )))
    }
}

impl<T, Spawner, S, B> Service<ServiceRequest> for VerifyMiddleware<T, Spawner, S>
where
    T: SignatureVerify + Clone + 'static,
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error> + 'static,
    S::Error: 'static,
    B: MessageBody + 'static,
{
    type Response = ServiceResponse<B>;
    type Error = actix_web::Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
        self.0.poll_ready(cx)
    }

    fn call(&self, req: ServiceRequest) -> Self::Future {
        let span = tracing::info_span!(
            "Signature Verification",
            signature.kind = tracing::field::Empty,
            signature.expected_kind = tracing::field::display(&self.2),
        );
        let authorization = req.headers().get("Authorization").is_some();
        let signature = req.headers().get("Signature").is_some();

        if authorization {
            span.record("signature.kind", tracing::field::display("Authorization"));

            if self.2.is_authorization() {
                return self.handle(span, req);
            }
        } else if signature {
            span.record("signature.kind", tracing::field::display("Signature"));

            if self.2.is_signature() {
                return self.handle(span, req);
            }
        } else {
            span.record("signature.kind", tracing::field::display("None"));
        }

        Box::pin(self.0.call(req))
    }
}

impl ResponseError for VerifyError {
    fn status_code(&self) -> StatusCode {
        StatusCode::BAD_REQUEST
    }

    fn error_response(&self) -> HttpResponse {
        HttpResponse::new(self.status_code())
    }
}