BLOG-86 Checking authentication before uploading image (#101)
All checks were successful
Frontend CI / build (push) Successful in 1m8s
All checks were successful
Frontend CI / build (push) Successful in 1m8s
### Description This PR introduces a generic authentication middleware to protect application routes. The primary goal is to prevent unauthenticated users from uploading images. #### Changes Implemented * **Authentication Middleware**: * Created a new `auth_middleware` that checks the user's session for a valid `user_id`. * If a `user_id` exists, it's added to the request extensions, making it available to downstream handlers. * **`UserId` Extractor**: * A `UserId` type that implements `FromRequest` has been added. * This allows route handlers to declaratively require authentication by simply adding `user_id: UserId` as a parameter. If the user is not logged in, the extractor automatically returns an `ErrorUnauthorized` response. * **Route Protection**: * The `upload_image_handler` now includes the `UserId` extractor, securing the endpoint. * A new `/auth/me` route has been added for easily verifying the logged-in user's ID during development and testing. * **Minor Refinements**: * The `logout_handler` now uses `session.clear()` for more robust session termination. * Corrected the default Redis URL from `redis://127.0.1:6379` to `redis://127.0.0.1:6379`. ### Package Changes _No response_ ### Screenshots _No response_ ### Reference Resolves #86 ### Checklist - [x] A milestone is set - [x] The related issuse has been linked to this branch Reviewed-on: #101 Co-authored-by: SquidSpirit <squid@squidspirit.com> Co-committed-by: SquidSpirit <squid@squidspirit.com>
This commit is contained in:
parent
0d6810f3d5
commit
197d7773ef
1
backend/Cargo.lock
generated
1
backend/Cargo.lock
generated
@ -1664,6 +1664,7 @@ dependencies = [
|
||||
"actix-multipart",
|
||||
"actix-web",
|
||||
"async-trait",
|
||||
"auth",
|
||||
"futures",
|
||||
"log",
|
||||
"serde",
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub mod auth_middleware;
|
||||
pub mod auth_web_routes;
|
||||
|
||||
mod constants;
|
||||
|
33
backend/feature/auth/src/framework/web/auth_middleware.rs
Normal file
33
backend/feature/auth/src/framework/web/auth_middleware.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use std::future::{self, Ready};
|
||||
|
||||
use actix_session::SessionExt;
|
||||
use actix_web::{Error, FromRequest, HttpRequest, dev::Payload, error::ErrorUnauthorized};
|
||||
|
||||
use crate::framework::web::constants::SESSION_KEY_USER_ID;
|
||||
|
||||
pub struct UserId(i32);
|
||||
|
||||
impl UserId {
|
||||
pub fn get(&self) -> i32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRequest for UserId {
|
||||
type Error = Error;
|
||||
type Future = Ready<Result<Self, Self::Error>>;
|
||||
|
||||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||
let user_id_result = req.get_session().get::<i32>(SESSION_KEY_USER_ID);
|
||||
|
||||
let user_id = match user_id_result {
|
||||
Ok(id) => id,
|
||||
_ => return future::ready(Err(ErrorUnauthorized(""))),
|
||||
};
|
||||
|
||||
match user_id {
|
||||
Some(id) => future::ready(Ok(UserId(id))),
|
||||
None => future::ready(Err(ErrorUnauthorized(""))),
|
||||
}
|
||||
}
|
||||
}
|
@ -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.get()))
|
||||
}
|
||||
|
@ -11,3 +11,5 @@ futures.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
sqlx.workspace = true
|
||||
|
||||
auth.workspace = true
|
||||
|
@ -1,5 +1,6 @@
|
||||
use actix_multipart::Multipart;
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
use auth::framework::web::auth_middleware::UserId;
|
||||
use futures::StreamExt;
|
||||
|
||||
use crate::{
|
||||
@ -18,6 +19,7 @@ pub fn configure_image_routes(cfg: &mut web::ServiceConfig) {
|
||||
async fn upload_image_handler(
|
||||
image_controller: web::Data<dyn ImageController>,
|
||||
mut payload: Multipart,
|
||||
_: UserId,
|
||||
) -> impl Responder {
|
||||
let mut image_request_dto: Option<ImageRequestDto> = None;
|
||||
|
||||
|
@ -20,7 +20,7 @@ impl SessionConfiguration {
|
||||
let session_key = Key::from(&session_key_bytes);
|
||||
|
||||
let redis_url =
|
||||
std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.1:6379".to_string());
|
||||
std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
|
||||
|
||||
Self {
|
||||
session_key,
|
||||
|
@ -63,6 +63,7 @@ fn create_app(
|
||||
let container = Container::new(db_pool, http_client, configuration);
|
||||
|
||||
App::new()
|
||||
// The middlewares are executed in opposite order as registration.
|
||||
.wrap(session_middleware_builder.build())
|
||||
.app_data(web::Data::from(container.auth_controller))
|
||||
.app_data(web::Data::from(container.image_controller))
|
||||
|
Loading…
x
Reference in New Issue
Block a user