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!")
}