use actix_web::{http::StatusCode, web, App, HttpRequest, HttpResponse, HttpServer, ResponseError}; use http_signature_normalization_actix_extractor::{ Algorithm, Config, ConfigGenerator, DeprecatedAlgorithm, Signed, VerifyKey, }; use sha2::Sha256; #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| App::new().route("/", web::post().to(protected))) .bind("127.0.0.1:8010")? .run() .await } async fn protected(signed_request: Signed) -> &'static str { let (value, signature) = signed_request.into_parts(); println!("{}", value); println!("{:#?}", signature); "hewwo, mr obama" } pub struct Cfg; #[derive(Debug)] pub struct Key; #[derive(Debug, thiserror::Error)] pub enum VerifyError { #[error("Unsupported algorithm")] Algorithm, #[error("Couldn't decode signature")] Decode, #[error("Invalid key")] Key, } impl ConfigGenerator for Cfg { fn config() -> Config { Config::new().require_header("accept") } } #[async_trait::async_trait(?Send)] impl VerifyKey for Key { type Error = VerifyError; async fn init( _: &HttpRequest, key_id: &str, algorithm: Option<&Algorithm>, ) -> Result { match algorithm { Some(Algorithm::Hs2019 | Algorithm::Deprecated(DeprecatedAlgorithm::RsaSha256)) => (), _ => return Err(VerifyError::Algorithm), }; if key_id != "my-key-id" { return Err(VerifyError::Key); } Ok(Key) } fn verify(&mut self, signature: &str, signing_string: &str) -> Result { use subtle::ConstantTimeEq; let decoded = base64::decode(&signature).map_err(|_| VerifyError::Decode)?; Ok(decoded.ct_eq(signing_string.as_bytes()).into()) } } impl ResponseError for VerifyError { fn status_code(&self) -> StatusCode { StatusCode::BAD_REQUEST } fn error_response(&self) -> HttpResponse { HttpResponse::BadRequest().finish() } }