diff --git a/Cargo.toml b/Cargo.toml index c28feb6..027252d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ homepage = "https://github.com/LeoniePhiline/axum-csrf-sync-pattern" license = "Apache-2.0" readme = "README.md" -version = "0.1.0" +version = "0.1.1" edition = "2021" [badges] diff --git a/README.md b/README.md index 61891cb..1eac830 100644 --- a/README.md +++ b/README.md @@ -91,37 +91,40 @@ In each example directory, execute `cargo run`, then open [http://127.0.0.1:3000 Configure your session and CSRF protection layer in your backend application: ```rust +use async_session::MemoryStore; +use axum::{ + body::Body, + http::StatusCode, + routing::{get, Router}, +}; +use axum_csrf_sync_pattern::{CsrfSynchronizerTokenLayer, RegenerateToken}; +use axum_sessions::SessionLayer; use rand::RngCore; let mut secret = [0; 64]; rand::thread_rng().try_fill_bytes(&mut secret).unwrap(); -async fn handler() -> axum::http::StatusCode { - axum::http::StatusCode::OK +async fn handler() -> StatusCode { + StatusCode::OK } -let app = axum::Router::new() - .route("/", axum::routing::get(handler).post(handler)) +let app = Router::new() + .route("/", get(handler).post(handler)) .layer( - axum_csrf_sync_pattern::CsrfSynchronizerTokenLayer::default() + CsrfSynchronizerTokenLayer::default() // Optionally, configure the layer with the following options: // Default: RegenerateToken::PerSession - .regenerate(axum_csrf_sync_pattern::RegenerateToken::PerUse) + .regenerate(RegenerateToken::PerUse) // Default: "X-CSRF-TOKEN" - .request_header("X-Custom-CSRF-Token-Client-Request-Header") + .request_header("X-Custom-Request-Header") // Default: "X-CSRF-TOKEN" - .response_header("X-Custom-CSRF-Token-Server-Response-Header") + .response_header("X-Custom-Response-Header") // Default: "_csrf_token" - .session_key("_custom_csrf_token_session_key") + .session_key("_custom_session_key") ) - .layer( - axum_sessions::SessionLayer::new( - async_session::MemoryStore::new(), - &secret - ) - ); + .layer(SessionLayer::new(MemoryStore::new(), &secret)); // Use hyper to run `app` as service and expose on a local port or socket. @@ -139,7 +142,7 @@ Receive the token and send same-site requests, using your custom header: const test = async () => { // Receive CSRF token (Default response header name: 'X-CSRF-TOKEN') const token = (await fetch("/")).headers.get( - "X-Custom-CSRF-Token-Server-Response-Header" + "X-Custom-Response-Header" ); // Submit data using the token @@ -148,7 +151,7 @@ const test = async () => { headers: { "Content-Type": "application/json", // Default request header name: 'X-CSRF-TOKEN' - "X-Custom-CSRF-Token-Client-Request-Header": token, + "X-Custom-Request-Header": token, }, body: JSON.stringify({ /* ... */ @@ -168,32 +171,36 @@ In each example directory, execute `cargo run`, then open [http://127.0.0.1:3000 Configure your CORS layer, session and CSRF protection layer in your backend application: ```rust +use async_session::MemoryStore; +use axum::{ + body::Body, + http::{header, Method, StatusCode}, + routing::{get, Router}, +}; +use axum_csrf_sync_pattern::{CsrfSynchronizerTokenLayer, RegenerateToken}; +use axum_sessions::SessionLayer; use rand::RngCore; +use tower_http::cors::{AllowOrigin, CorsLayer}; let mut secret = [0; 64]; rand::thread_rng().try_fill_bytes(&mut secret).unwrap(); -async fn handler() -> axum::http::StatusCode { - axum::http::StatusCode::OK +async fn handler() -> StatusCode { + StatusCode::OK } -let app = axum::Router::new() - .route("/", axum::routing::get(handler).post(handler)) +let app = Router::new() + .route("/", get(handler).post(handler)) .layer( // See example above for custom layer configuration. - axum_csrf_sync_pattern::CsrfSynchronizerTokenLayer::default() - ) - .layer( - axum_sessions::SessionLayer::new( - async_session::MemoryStore::new(), - &secret - ) + CsrfSynchronizerTokenLayer::default() ) + .layer(SessionLayer::new(MemoryStore::new(), &secret)) .layer( - tower_http::cors::CorsLayer::new() - .allow_origin(tower_http::cors::AllowOrigin::list(["https://www.example.com".parse().unwrap()])) - .allow_methods([axum::http::Method::GET, axum::http::Method::POST]) - .allow_headers([axum::http::header::CONTENT_TYPE, "X-CSRF-TOKEN".parse().unwrap()]) + CorsLayer::new() + .allow_origin(AllowOrigin::list(["https://www.example.com".parse().unwrap()])) + .allow_methods([Method::GET, Method::POST]) + .allow_headers([header::CONTENT_TYPE, "X-CSRF-TOKEN".parse().unwrap()]) .allow_credentials(true) .expose_headers(["X-CSRF-TOKEN".parse().unwrap()]), ); diff --git a/examples/cross-site/Cargo.toml b/examples/cross-site/Cargo.toml index 3b68057..01cf73c 100644 --- a/examples/cross-site/Cargo.toml +++ b/examples/cross-site/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cross-site" authors = ["LeoniePhiline "] -version = "0.1.0" +version = "0.1.1" edition = "2021" publish = false diff --git a/examples/cross-site/src/main.rs b/examples/cross-site/src/main.rs index 11cafb9..cf85312 100644 --- a/examples/cross-site/src/main.rs +++ b/examples/cross-site/src/main.rs @@ -1,9 +1,17 @@ +use std::net::SocketAddr; + +use async_session::MemoryStore; use axum::{ http::{header, Method, StatusCode}, response::IntoResponse, + routing::{get, Router}, + Server, }; +use axum_csrf_sync_pattern::CsrfSynchronizerTokenLayer; +use axum_sessions::SessionLayer; use color_eyre::eyre::{self, eyre, WrapErr}; use rand::RngCore; +use tower_http::cors::{AllowOrigin, CorsLayer}; #[tokio::main] async fn main() -> eyre::Result<()> { @@ -17,7 +25,7 @@ async fn main() -> eyre::Result<()> { .wrap_err("Failed to initialize tracing-subscriber.")?; let frontend = async { - let app = axum::Router::new().route("/", axum::routing::get(index)); + let app = Router::new().route("/", get(index)); // Visit "http://127.0.0.1:3000/" in your browser. serve(app, 3000).await; @@ -27,16 +35,13 @@ async fn main() -> eyre::Result<()> { let mut secret = [0; 64]; rand::thread_rng().try_fill_bytes(&mut secret).unwrap(); - let app = axum::Router::new() - .route("/", axum::routing::get(get_token).post(post_handler)) - .layer(axum_csrf_sync_pattern::CsrfSynchronizerTokenLayer::default()) - .layer(axum_sessions::SessionLayer::new( - async_session::MemoryStore::new(), - &secret, - )) + let app = Router::new() + .route("/", get(get_token).post(post_handler)) + .layer(CsrfSynchronizerTokenLayer::default()) + .layer(SessionLayer::new(MemoryStore::new(), &secret)) .layer( - tower_http::cors::CorsLayer::new() - .allow_origin(tower_http::cors::AllowOrigin::list([ + CorsLayer::new() + .allow_origin(AllowOrigin::list([ // Allow CORS requests from our frontend. "http://127.0.0.1:3000".parse().unwrap(), ])) @@ -44,7 +49,7 @@ async fn main() -> eyre::Result<()> { .allow_methods([Method::GET, Method::POST]) .allow_headers([ // Allow incoming CORS requests to use the Content-Type header, - axum::http::header::CONTENT_TYPE, + header::CONTENT_TYPE, // as well as the `CsrfSynchronizerTokenLayer` default request header. "X-CSRF-TOKEN".parse().unwrap(), ]) @@ -63,9 +68,9 @@ async fn main() -> eyre::Result<()> { Ok(()) } -async fn serve(app: axum::Router, port: u16) { - let addr = std::net::SocketAddr::from(([127, 0, 0, 1], port)); - axum::Server::bind(&addr) +async fn serve(app: Router, port: u16) { + let addr = SocketAddr::from(([127, 0, 0, 1], port)); + Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); diff --git a/examples/same-site/Cargo.toml b/examples/same-site/Cargo.toml index 84e6544..c85d14f 100644 --- a/examples/same-site/Cargo.toml +++ b/examples/same-site/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "same-site" authors = ["LeoniePhiline "] -version = "0.1.0" +version = "0.1.1" edition = "2021" publish = false diff --git a/examples/same-site/src/main.rs b/examples/same-site/src/main.rs index 07b6011..3cbae46 100644 --- a/examples/same-site/src/main.rs +++ b/examples/same-site/src/main.rs @@ -1,7 +1,12 @@ +use async_session::MemoryStore; use axum::{ http::{header, StatusCode}, response::IntoResponse, + routing::get, + Server, }; +use axum_csrf_sync_pattern::CsrfSynchronizerTokenLayer; +use axum_sessions::SessionLayer; use color_eyre::eyre::{self, eyre, WrapErr}; use rand::RngCore; @@ -20,15 +25,12 @@ async fn main() -> eyre::Result<()> { rand::thread_rng().try_fill_bytes(&mut secret).unwrap(); let app = axum::Router::new() - .route("/", axum::routing::get(index).post(handler)) - .layer(axum_csrf_sync_pattern::CsrfSynchronizerTokenLayer::default()) - .layer(axum_sessions::SessionLayer::new( - async_session::MemoryStore::new(), - &secret, - )); + .route("/", get(index).post(handler)) + .layer(CsrfSynchronizerTokenLayer::default()) + .layer(SessionLayer::new(MemoryStore::new(), &secret)); // Visit "http://127.0.0.1:3000/" in your browser. - axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + Server::bind(&"0.0.0.0:3000".parse().unwrap()) .serve(app.into_make_service()) .await .unwrap(); @@ -44,6 +46,6 @@ async fn index() -> impl IntoResponse { ) } -async fn handler() -> axum::http::StatusCode { +async fn handler() -> StatusCode { StatusCode::ACCEPTED } diff --git a/src/lib.rs b/src/lib.rs index 638ede7..e4c4ae5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,37 +78,40 @@ //! Configure your session and CSRF protection layer in your backend application: //! //! ```rust +//! use async_session::MemoryStore; +//! use axum::{ +//! body::Body, +//! http::StatusCode, +//! routing::{get, Router}, +//! }; +//! use axum_csrf_sync_pattern::{CsrfSynchronizerTokenLayer, RegenerateToken}; +//! use axum_sessions::SessionLayer; //! use rand::RngCore; //! //! let mut secret = [0; 64]; //! rand::thread_rng().try_fill_bytes(&mut secret).unwrap(); //! -//! async fn handler() -> axum::http::StatusCode { -//! axum::http::StatusCode::OK +//! async fn handler() -> StatusCode { +//! StatusCode::OK //! } //! -//! let app = axum::Router::new() -//! .route("/", axum::routing::get(handler).post(handler)) +//! let app = Router::new() +//! .route("/", get(handler).post(handler)) //! .layer( -//! axum_csrf_sync_pattern::CsrfSynchronizerTokenLayer::default() +//! CsrfSynchronizerTokenLayer::default() //! //! // Optionally, configure the layer with the following options: //! //! // Default: RegenerateToken::PerSession -//! .regenerate(axum_csrf_sync_pattern::RegenerateToken::PerUse) +//! .regenerate(RegenerateToken::PerUse) //! // Default: "X-CSRF-TOKEN" -//! .request_header("X-Custom-CSRF-Token-Client-Request-Header") +//! .request_header("X-Custom-Request-Header") //! // Default: "X-CSRF-TOKEN" -//! .response_header("X-Custom-CSRF-Token-Server-Response-Header") +//! .response_header("X-Custom-Response-Header") //! // Default: "_csrf_token" -//! .session_key("_custom_csrf_token_session_key") +//! .session_key("_custom_session_key") //! ) -//! .layer( -//! axum_sessions::SessionLayer::new( -//! async_session::MemoryStore::new(), -//! &secret -//! ) -//! ); +//! .layer(SessionLayer::new(MemoryStore::new(), &secret)); //! //! // Use hyper to run `app` as service and expose on a local port or socket. //! @@ -125,7 +128,7 @@ //! ```javascript //! const test = async () => { //! // Receive CSRF token (Default response header name: 'X-CSRF-TOKEN') -//! const token = (await fetch('/')).headers.get('X-Custom-CSRF-Token-Server-Response-Header'); +//! const token = (await fetch('/')).headers.get('X-Custom-Response-Header'); //! //! // Submit data using the token //! await fetch('/', { @@ -133,7 +136,7 @@ //! headers: { //! 'Content-Type': 'application/json', //! // Default request header name: 'X-CSRF-TOKEN' -//! 'X-Custom-CSRF-Token-Client-Request-Header': token, +//! 'X-Custom-Request-Header': token, //! }, //! body: JSON.stringify({ /* ... */ }), //! }); @@ -147,32 +150,36 @@ //! Configure your CORS layer, session and CSRF protection layer in your backend application: //! //! ```rust +//! use async_session::MemoryStore; +//! use axum::{ +//! body::Body, +//! http::{header, Method, StatusCode}, +//! routing::{get, Router}, +//! }; +//! use axum_csrf_sync_pattern::{CsrfSynchronizerTokenLayer, RegenerateToken}; +//! use axum_sessions::SessionLayer; //! use rand::RngCore; +//! use tower_http::cors::{AllowOrigin, CorsLayer}; //! //! let mut secret = [0; 64]; //! rand::thread_rng().try_fill_bytes(&mut secret).unwrap(); //! -//! async fn handler() -> axum::http::StatusCode { -//! axum::http::StatusCode::OK +//! async fn handler() -> StatusCode { +//! StatusCode::OK //! } //! -//! let app = axum::Router::new() -//! .route("/", axum::routing::get(handler).post(handler)) +//! let app = Router::new() +//! .route("/", get(handler).post(handler)) //! .layer( //! // See example above for custom layer configuration. -//! axum_csrf_sync_pattern::CsrfSynchronizerTokenLayer::default() -//! ) -//! .layer( -//! axum_sessions::SessionLayer::new( -//! async_session::MemoryStore::new(), -//! &secret -//! ) +//! CsrfSynchronizerTokenLayer::default() //! ) +//! .layer(SessionLayer::new(MemoryStore::new(), &secret)) //! .layer( -//! tower_http::cors::CorsLayer::new() -//! .allow_origin(tower_http::cors::AllowOrigin::list(["https://www.example.com".parse().unwrap()])) -//! .allow_methods([axum::http::Method::GET, axum::http::Method::POST]) -//! .allow_headers([axum::http::header::CONTENT_TYPE, "X-CSRF-TOKEN".parse().unwrap()]) +//! CorsLayer::new() +//! .allow_origin(AllowOrigin::list(["https://www.example.com".parse().unwrap()])) +//! .allow_methods([Method::GET, Method::POST]) +//! .allow_headers([header::CONTENT_TYPE, "X-CSRF-TOKEN".parse().unwrap()]) //! .allow_credentials(true) //! .expose_headers(["X-CSRF-TOKEN".parse().unwrap()]), //! );