From 22e01d10dc36570dbe85c454446a1040e05b05be Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 23 Feb 2025 21:02:42 +0100 Subject: [PATCH] feat: implement basic auth in middleware --- src/server/gpodder/auth.rs | 80 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/src/server/gpodder/auth.rs b/src/server/gpodder/auth.rs index a839c36..a304392 100644 --- a/src/server/gpodder/auth.rs +++ b/src/server/gpodder/auth.rs @@ -1,7 +1,10 @@ use axum::{ - extract::{Path, State}, + extract::{Path, Request, State}, + http::StatusCode, + middleware::Next, + response::{IntoResponse, Response}, routing::post, - Router, + RequestExt, Router, }; use axum_extra::{ extract::{ @@ -69,7 +72,7 @@ async fn post_logout( tokio::task::spawn_blocking(move || { if let Some(session) = Session::by_id(&ctx.pool, session_id)? { - let user = session.user(&ctx.pool)?; + let user = session.user(&ctx.pool)?.ok_or(AppError::NotFound)?; // The requested user to logout should be the same as the one linked to the session // ID @@ -90,3 +93,74 @@ async fn post_logout( Ok(jar) } } + +/// This middleware accepts +pub async fn auth_middleware(State(ctx): State, mut req: Request, next: Next) -> Response { + // SAFETY: this extractor's error type is Infallible + let jar: CookieJar = req.extract_parts().await.unwrap(); + tracing::debug!("{:?}", jar); + let mut auth_user = None; + let mut new_session_id = None; + + if let Some(session_id) = jar + .get(SESSION_ID_COOKIE) + .and_then(|c| c.value().parse::().ok()) + { + match tokio::task::spawn_blocking(move || Session::user_from_id(&ctx.pool, session_id)) + .await + .unwrap() + .map_err(AppError::Db) + { + Ok(user) => { + auth_user = user; + } + Err(err) => { + return err.into_response(); + } + }; + } else if let Ok(auth) = req + .extract_parts::>>() + .await + { + match tokio::task::spawn_blocking(move || { + let user = User::by_username(&ctx.pool, auth.username())?.ok_or(AppError::NotFound)?; + + if user.verify_password(auth.password()) { + Ok((Session::new_for_user(&ctx.pool, user.id)?, user)) + } else { + Err(AppError::Unauthorized) + } + }) + .await + .unwrap() + { + Ok((session, user)) => { + auth_user = Some(user); + new_session_id = Some(session.id); + } + Err(err) => { + return err.into_response(); + } + } + } + + if let Some(user) = auth_user { + req.extensions_mut().insert(user); + let res = next.run(req).await; + + if let Some(session_id) = new_session_id { + ( + jar.add( + Cookie::build((SESSION_ID_COOKIE, session_id.to_string())) + .expires(Expiration::Session), + ), + res, + ) + .into_response() + } else { + res + } + } else { + StatusCode::UNAUTHORIZED.into_response() + } +}