From 0e8eb809a525538fbe1c239a469b63edf617ba16 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Fri, 1 Aug 2025 17:40:34 +0800 Subject: [PATCH] BLOG-86 feat: implement authentication middleware and update routes for user session management --- backend/feature/auth/src/framework/web.rs | 1 + .../auth/src/framework/web/auth_middleware.rs | 49 +++++++++++++++++++ .../auth/src/framework/web/auth_web_routes.rs | 15 ++++-- backend/server/src/main.rs | 7 ++- 4 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 backend/feature/auth/src/framework/web/auth_middleware.rs diff --git a/backend/feature/auth/src/framework/web.rs b/backend/feature/auth/src/framework/web.rs index 8853d00..54b1487 100644 --- a/backend/feature/auth/src/framework/web.rs +++ b/backend/feature/auth/src/framework/web.rs @@ -1,3 +1,4 @@ +pub mod auth_middleware; pub mod auth_web_routes; mod constants; diff --git a/backend/feature/auth/src/framework/web/auth_middleware.rs b/backend/feature/auth/src/framework/web/auth_middleware.rs new file mode 100644 index 0000000..ac183a4 --- /dev/null +++ b/backend/feature/auth/src/framework/web/auth_middleware.rs @@ -0,0 +1,49 @@ +use std::future::{self, Ready}; + +use actix_session::SessionExt; +use actix_web::{ + Error, FromRequest, HttpMessage, HttpRequest, + body::MessageBody, + dev::{Payload, ServiceRequest, ServiceResponse}, + error::ErrorUnauthorized, + middleware::Next, +}; + +use crate::framework::web::constants::SESSION_KEY_USER_ID; + +pub async fn auth_middleware( + req: ServiceRequest, + next: Next, +) -> Result, Error> { + let session = req.get_session(); + let user_id = session.get::(SESSION_KEY_USER_ID); + + if user_id.is_err() { + return next.call(req).await; + } + + let user_id = user_id.unwrap(); + if user_id.is_none() { + return next.call(req).await; + } + + let user_id = user_id.unwrap(); + req.extensions_mut().insert(user_id); + next.call(req).await +} + +pub struct UserId(pub i32); + +impl FromRequest for UserId { + type Error = Error; + type Future = Ready>; + + fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + let user_id = req.extensions().get::().cloned(); + + match user_id { + Some(id) => future::ready(Ok(UserId(id))), + None => future::ready(Err(ErrorUnauthorized(""))), + } + } +} diff --git a/backend/feature/auth/src/framework/web/auth_web_routes.rs b/backend/feature/auth/src/framework/web/auth_web_routes.rs index f0ab70d..af43672 100644 --- a/backend/feature/auth/src/framework/web/auth_web_routes.rs +++ b/backend/feature/auth/src/framework/web/auth_web_routes.rs @@ -6,8 +6,9 @@ use crate::{ auth_controller::AuthController, oidc_callback_query_dto::OidcCallbackQueryDto, }, application::error::auth_error::AuthError, - framework::web::constants::{ - SESSION_KEY_AUTH_NONCE, SESSION_KEY_AUTH_STATE, SESSION_KEY_USER_ID, + framework::web::{ + auth_middleware::UserId, + constants::{SESSION_KEY_AUTH_NONCE, SESSION_KEY_AUTH_STATE, SESSION_KEY_USER_ID}, }, }; @@ -18,6 +19,8 @@ pub fn configure_auth_routes(cfg: &mut web::ServiceConfig) { .route("/callback", web::get().to(oidc_callback_handler)) .route("/logout", web::get().to(logout_handler)), ); + + cfg.service(web::resource("/me").route(web::get().to(get_logged_in_user_handler))); } async fn oidc_login_handler( @@ -92,10 +95,12 @@ async fn oidc_callback_handler( } async fn logout_handler(session: Session) -> impl Responder { - session.remove(SESSION_KEY_AUTH_STATE); - session.remove(SESSION_KEY_AUTH_NONCE); - session.remove(SESSION_KEY_USER_ID); + session.clear(); HttpResponse::Found() .append_header((header::LOCATION, "/")) .finish() } + +async fn get_logged_in_user_handler(user_id: UserId) -> impl Responder { + HttpResponse::Ok().body(format!("Logged in user ID: {}", user_id.0)) +} diff --git a/backend/server/src/main.rs b/backend/server/src/main.rs index 17fe620..52a36a4 100644 --- a/backend/server/src/main.rs +++ b/backend/server/src/main.rs @@ -5,9 +5,12 @@ use actix_web::{ App, Error, HttpServer, body::MessageBody, dev::{ServiceFactory, ServiceRequest, ServiceResponse}, + middleware::from_fn, web, }; -use auth::framework::web::auth_web_routes::configure_auth_routes; +use auth::framework::web::{ + auth_middleware::auth_middleware, auth_web_routes::configure_auth_routes, +}; use image::framework::web::image_web_routes::configure_image_routes; use openidconnect::reqwest; use post::framework::web::post_web_routes::configure_post_routes; @@ -63,6 +66,8 @@ fn create_app( let container = Container::new(db_pool, http_client, configuration); App::new() + // The middlewares are executed in opposite order as registration. + .wrap(from_fn(auth_middleware)) .wrap(session_middleware_builder.build()) .app_data(web::Data::from(container.auth_controller)) .app_data(web::Data::from(container.image_controller))