Crate http_signature_normalization_actix[][src]

Expand description

Integration of Http Signature Normalization with Actix Web

This library provides middlewares for verifying HTTP Signature headers and, optionally, Digest headers with the digest feature enabled. It also extends awc’s ClientRequest type to add signatures and digests to the request

Use it in a server

use actix_web::{http::StatusCode, web, App, HttpResponse, HttpServer, ResponseError};
use http_signature_normalization_actix::prelude::*;
use sha2::{Digest, Sha256};
use std::future::{ready, Ready};

#[derive(Clone, Debug)]
struct MyVerify;

impl SignatureVerify for MyVerify {
    type Error = MyError;
    type Future = Ready<Result<bool, Self::Error>>;

    fn signature_verify(
        &mut self,
        algorithm: Option<Algorithm>,
        key_id: String,
        signature: String,
        signing_string: String,
    ) -> Self::Future {
        match algorithm {
            Some(Algorithm::Hs2019) => (),
            _ => return ready(Err(MyError::Algorithm)),
        };

        if key_id != "my-key-id" {
            return ready(Err(MyError::Key));
        }

        let decoded = match base64::decode(&signature) {
            Ok(decoded) => decoded,
            Err(_) => return ready(Err(MyError::Decode)),
        };

        ready(Ok(decoded == signing_string.as_bytes()))
    }
}

async fn index((_, sig_verified): (DigestVerified, SignatureVerified)) -> &'static str {
    println!("Signature verified for {}", sig_verified.key_id());
    "Eyyyyup"
}

#[actix_rt::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config::default();

    HttpServer::new(move || {
        App::new()
            .wrap(VerifyDigest::new(Sha256::new()).optional())
            .wrap(
                VerifySignature::new(MyVerify, config.clone())
                    .authorization()
                    .optional(),
            )
            .route("/", web::post().to(index))
    })
    .bind("127.0.0.1:8010")?
    .run()
    .await?;

    Ok(())
}

#[derive(Debug, thiserror::Error)]
enum MyError {
    #[error("Failed to verify, {}", _0)]
    Verify(#[from] PrepareVerifyError),

    #[error("Unsupported algorithm")]
    Algorithm,

    #[error("Couldn't decode signature")]
    Decode,

    #[error("Invalid key")]
    Key,
}

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

    fn error_response(&self) -> HttpResponse {
        HttpResponse::BadRequest().finish()
    }
}

Use it in a client

use actix_web::error::BlockingError;
use awc::Client;
use http_signature_normalization_actix::prelude::*;
use sha2::{Digest, Sha256};

#[actix_rt::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config::default();
    let digest = Sha256::new();

    let mut response = Client::default()
        .post("http://127.0.0.1:8010/")
        .header("User-Agent", "Actix Web")
        .set(actix_web::http::header::Date(SystemTime::now().into()))
        .signature_with_digest(config, "my-key-id", digest, "Hewwo-owo", |s| {
            println!("Signing String\n{}", s);
            Ok(base64::encode(s)) as Result<_, MyError>
        })
        .await?
        .send()
        .await
        .map_err(|e| {
            eprintln!("Error, {}", e);
            MyError::SendRequest
        })?;

    let body = response.body().await.map_err(|e| {
        eprintln!("Error, {}", e);
        MyError::Body
    })?;

    println!("{:?}", body);
    Ok(())
}

#[derive(Debug, thiserror::Error)]
pub enum MyError {
    #[error("Failed to create signing string, {0}")]
    Convert(#[from] PrepareSignError),

    #[error("Failed to create header, {0}")]
    Header(#[from] InvalidHeaderValue),

    #[error("Failed to send request")]
    SendRequest,

    #[error("Failed to retrieve request body")]
    Body,

    #[error("Blocking operation was canceled")]
    Canceled,
}

impl From<BlockingError> for MyError {
    fn from(_: BlockingError) -> Self {
        MyError::Canceled,
    }
}

Modules

Types for signing requests with Actix Web

Types and Traits for creating and verifying Digest headers

Types for verifying requests with Actix Web

Useful types and traits for using this library in Actix Web

Types for Verifying an HTTP Signature

Structs

Configuration for signing and verifying signatures

Failed to build a signing string due to missing required headers

Enums

An error when preparing to sign a request

An error when preparing to verify a request

Traits

A trait implemented by the awc ClientRequest type to add an HTTP signature to the request

A trait for verifying signatures