1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
//! Verify that incoming requests are from Alexa for custom, webservice skills.  
//!
//! - Confirmed working with the Alexa [certification functional test](https://developer.amazon.com/docs/devconsole/test-and-submit-your-skill.html).
//!
//! - Built using the [Developer Documentation](https://developer.amazon.com/docs/custom-skills/host-a-custom-skill-as-a-web-service.html#manually-verify-request-sent-by-alexa)
//! and [Python Alexa SDK](https://github.com/alexa/alexa-skills-kit-sdk-for-python/tree/master/ask-sdk-webservice-support)
//! as reference.
//!
//! # Features
//! Both sync and async clients are provided by default. These are behind feature
//! flags `sync` or `async`, respectively.
//!
//! - `sync` provides `RequestVerifier` client
//! - `async` provides `RequestVerifierAsync` client
//!
//! # Using
//! Example using [Rouille](https://github.com/tomaka/rouille) server
//! and [alexa_sdk](https://github.com/tarkah/alexa_rust) for request deserialization
//!
//! ```rust
//! use crate::skill::process_request; // Entry point to custom skill
//! use alexa_verifier::RequestVerifier; // Struct provided by this crate
//! use log::{debug, error, info};
//! use rouille::{router, Request, Response};
//! use std::io::Read;
//!
//! fn note_routes(request: &Request, verifier: &RequestVerifier) -> Response {
//!     router!(request,
//!         (POST) (/) => {
//!             info!("Request received...");
//!
//!             // Get request body data
//!             let mut body = request.data().unwrap();
//!             let mut body_bytes: Vec<u8> = vec![];
//!             body.read_to_end(&mut body_bytes).unwrap();
//!
//!             // Get needed headers, default to blank (will cause verification to fail)
//!             let signature_cert_chain_url = request.header("SignatureCertChainUrl").unwrap_or("");
//!             let signature = request.header("Signature").unwrap_or("");
//!
//!             // Deserialize using alexa_sdk::Request
//!             let _request = serde_json::from_slice::<alexa_sdk::Request>(&body_bytes);
//!             if let Err(e) = _request {
//!                 error!("Could not deserialize request");
//!                 error!("{:?}", e);
//!                 let response = Response::empty_400();
//!                 info!("Sending back response...");
//!                 debug!("{:?}", response);
//!                 return response;
//!             }
//!             let request = _request.unwrap();
//!             debug!("{:?}", request);
//!
//!             // alexa-verifier used here, return 400 if verification fails
//!             if verifier
//!                 .verify(
//!                     signature_cert_chain_url,
//!                     signature,
//!                     &body_bytes,
//!                     request.body.timestamp.as_str(),
//!                     None
//!                 ).is_err() {
//!                     error!("Could not validate request came from Alexa");
//!                     let response = Response::empty_400();
//!                     info!("Sending back response...");
//!                     debug!("{:?}", response);
//!                     return response;
//!                 };
//!             debug!("Request is validated...");
//!
//!             // Entry point custom to skill, returning alexa_sdk::Response
//!             let response = Response::json(&process_request(request));
//!             info!("Sending back response...");
//!             debug!("{:?}", response);
//!             response
//!     },
//!         _ => Response::empty_404()
//!     )
//! }
//!
//! pub fn run() -> std::io::Result<()> {
//!     info!("Starting server on 0.0.0.0:8086");
//!     let verifier = RequestVerifier::new();
//!
//!     rouille::start_server("0.0.0.0:8086", move |request| {
//!         note_routes(&request, &verifier)
//!     });
//! }
//! ```
//!

mod constants;
mod error;
mod normalize;

#[cfg(feature = "sync")]
mod sync;
#[cfg(feature = "sync")]
pub use sync::RequestVerifier;

#[cfg(feature = "async")]
mod r#async;
#[cfg(feature = "async")]
pub use r#async::RequestVerifierAsync;