[][src]Crate http_signature_normalization_actix

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 actix_web's ClientRequest type to add signatures and digests to the request

Use it in a server

This example is not tested
use actix::System;
use actix_web::{web, App, HttpResponse, HttpServer, ResponseError};
use failure::Fail;
use http_signature_normalization_actix::{prelude::*, verify::Algorithm};
use sha2::{Digest, Sha256};

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

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

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

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

        let decoded = base64::decode(signature).map_err(|_| MyError::Decode)?;

        // In a real system, you'd want to actually verify a signature, not just check for
        // byte equality
        Ok(decoded == signing_string.as_bytes())
    }
}

fn index(_: (DigestVerified, SignatureVerified)) -> &'static str {
    "Eyyyyup"
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let sys = System::new("server-example");

    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")?
    .start();

    sys.run()?;
    Ok(())
}

#[derive(Debug, Fail)]
enum MyError {
    #[fail(display = "Failed to verify, {}", _0)]
    Verify(#[cause] PrepareVerifyError),

    #[fail(display = "Unsupported algorithm")]
    Algorithm,

    #[fail(display = "Couldn't decode signature")]
    Decode,

    #[fail(display = "Invalid key")]
    Key,
}

impl ResponseError for MyError {
    fn error_response(&self) -> HttpResponse {
        HttpResponse::BadRequest().finish()
    }

    fn render_response(&self) -> HttpResponse {
        self.error_response()
    }
}

impl From<PrepareVerifyError> for MyError {
    fn from(e: PrepareVerifyError) -> Self {
        MyError::Verify(e)
    }
}

Use it in a client

This example is not tested
use actix::System;
use actix_web::client::Client;
use failure::Fail;
use futures::future::{lazy, Future};
use http_signature_normalization_actix::prelude::*;
use sha2::{Digest, Sha256};

fn main() {
    System::new("client-example")
        .block_on(lazy(|| {
            let config = Config::default();
            let mut digest = Sha256::new();

            Client::default()
                .post("http://127.0.0.1:8010/")
                .header("User-Agent", "Actix Web")
                .authorization_signature_with_digest(
                    &config,
                    "my-key-id",
                    &mut digest,
                    "Hewwo-owo",
                    |s| {
                        // In a real-world system, you'd actually want to sign the string,
                        // not just base64 encode it
                        Ok(base64::encode(s)) as Result<_, MyError>
                    },
                )
                .unwrap()
                .send()
                .map_err(|_| ())
                .and_then(|mut res| res.body().map_err(|_| ()))
                .map(|body| {
                    println!("{:?}", body);
                })
        }))
        .unwrap();
}

#[derive(Debug, Fail)]
pub enum MyError {
    #[fail(display = "Failed to read header, {}", _0)]
    Convert(#[cause] ToStrError),

    #[fail(display = "Failed to create header, {}", _0)]
    Header(#[cause] InvalidHeaderValue),
}

impl From<ToStrError> for MyError {
    fn from(e: ToStrError) -> Self {
        MyError::Convert(e)
    }
}

impl From<InvalidHeaderValue> for MyError {
    fn from(e: InvalidHeaderValue) -> Self {
        MyError::Header(e)
    }
}

Modules

create

Types for signing requests with Actix Web

digest

Types and Traits for creating and verifying Digest headers

middleware

Types for verifying requests with Actix Web

prelude

Useful types and traits for using this library in Actix Web

verify

Types for Verifying an HTTP Signature

Structs

Config

A thin wrapper around the underlying library's config type

Enums

PrepareVerifyError

An error when preparing to verify a request

Traits

Sign

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

SignatureVerify

A trait for verifying signatures