crissy 0.1.1

CSRF protection middleware for Axum
Documentation
use axum::{
    Form, Router,
    response::{Html, IntoResponse},
    routing::get,
};
use crissy::CsrfToken;
use serde::Deserialize;
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(route_index).post(route_post))
        .layer(axum::middleware::from_fn(crissy::middleware::cookie));
    let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
    println!("listening on http://{:?}", listener.local_addr().unwrap());
    axum::serve(listener, app.into_make_service())
        .await
        .unwrap();
}

async fn route_index(csrf: CsrfToken) -> impl IntoResponse {
    Html(format!(
        r#"
<form method="POST">
    <input type="hidden" name="csrf_token" value="{csrf}"/>
    <button type="submit">Submit (valid CSRF token)</button>
</form>
<form method="POST">
    <input type="hidden" name="csrf_token" value="nope!"/>
    <button type="submit">Submit (invalid CSRF token)</button>
</form>
<form method="POST">
    <button type="submit">Submit (no CSRF token)</button>
</form>
"#,
        csrf = csrf.expected_csrf_token,
    ))
}

#[derive(Deserialize)]
struct Body {
    csrf_token: String,
}
async fn route_post(csrf: CsrfToken, body: Form<Body>) -> Result<impl IntoResponse, crissy::Error> {
    csrf.validate(&body.csrf_token)?;
    Ok("validation successful!")
}