alexa-verifier 0.2.1

Verify that requests were sent by Alexa for custom webservice skills, in Rust
Documentation
# Alexa Rust Webservice Verifier
[![Crates.io](https://img.shields.io/crates/v/alexa-verifier)](https://crates.io/crates/alexa-verifier)
[![](https://docs.rs/alexa-verifier/badge.svg)](https://docs.rs/alexa-verifier)

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)
    });
}
```


License: MIT