From d1c86151058173327bd083021bddd0a97ae126d7 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sun, 24 Nov 2024 14:25:45 -0500 Subject: [PATCH] Add router layer to require authentication --- src/main.rs | 4 +++- src/util/mod.rs | 1 + src/util/require_auth.rs | 46 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/util/require_auth.rs diff --git a/src/main.rs b/src/main.rs index 25a5a61..12522a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,10 +14,11 @@ extern crate diesel_migrations; #[cfg(feature = "ssr")] #[tokio::main] async fn main() { - use axum::{routing::get, Router, extract::Path}; + use axum::{routing::get, Router, extract::Path, middleware::from_fn}; use leptos::*; use leptos_axum::{generate_route_list, LeptosRoutes}; use libretunes::app::*; + use libretunes::util::require_auth::require_auth_middleware; use libretunes::fileserv::{file_and_error_handler, get_asset_file, get_static_file, AssetType}; use axum_login::tower_sessions::SessionManagerLayer; use tower_sessions_redis_store::{fred::prelude::*, RedisStore}; @@ -63,6 +64,7 @@ async fn main() { .route("/assets/audio/:song", get(|Path(song) : Path| get_asset_file(song, AssetType::Audio))) .route("/assets/images/:image", get(|Path(image) : Path| get_asset_file(image, AssetType::Image))) .route("/assets/*uri", get(|uri| get_static_file(uri, ""))) + .layer(from_fn(require_auth_middleware)) .layer(auth_layer) .fallback(file_and_error_handler) .with_state(leptos_options); diff --git a/src/util/mod.rs b/src/util/mod.rs index d7ad020..661d1e9 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -3,6 +3,7 @@ use cfg_if::cfg_if; cfg_if! { if #[cfg(feature = "ssr")] { pub mod audio; + pub mod require_auth; } } diff --git a/src/util/require_auth.rs b/src/util/require_auth.rs new file mode 100644 index 0000000..1d630c8 --- /dev/null +++ b/src/util/require_auth.rs @@ -0,0 +1,46 @@ +use axum::extract::Request; +use axum::response::Response; +use axum::body::Body; +use axum::middleware::Next; +use axum_login::AuthSession; +use http::StatusCode; + +use crate::auth_backend::AuthBackend; + +use axum::extract::FromRequestParts; + +// Things in pkg/ are allowed automatically. This includes the CSS/JS/WASM files +const ALLOWED_PATHS: [&str; 5] = ["/login", "/signup", "/api/login", "/api/signup", "/favicon.ico"]; + +/** + * Middleware to require authentication for all paths except those in ALLOWED_PATHS + * + * If a user is not authenticated, they will be redirected to the login page + */ +pub async fn require_auth_middleware(req: Request, next: Next) -> Result, (StatusCode, &'static str)> { + let path = req.uri().path(); + + if !ALLOWED_PATHS.iter().any(|&x| x == path) { + let (mut parts, body) = req.into_parts(); + + let auth_session = AuthSession::::from_request_parts(&mut parts, &()) + .await?; + + if auth_session.user.is_none() { + let response = Response::builder() + .status(StatusCode::TEMPORARY_REDIRECT) + .header("Location", "/login") + .body(Body::empty()) + .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Failed to build response"))?; + + return Ok(response); + } + + let req = Request::from_parts(parts, body); + let response = next.run(req).await; + Ok(response) + } else { + let response = next.run(req).await; + Ok(response) + } +}