Skip to main content

arc_web/http/middlewares/
auth_middleware.rs

1use crate::helpers::session::is_authenticated;
2use actix_session::SessionExt;
3use actix_web::body::EitherBody;
4use actix_web::{
5    dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
6    Error, HttpResponse,
7};
8use futures_util::future::LocalBoxFuture;
9use std::future::{ready, Ready};
10
11/// Session-based authentication middleware. Redirects unauthenticated requests to `/signin`.
12/// Checks for cached user data in the session to avoid database queries on every request.
13pub struct AuthMiddleware;
14
15impl<S, B> Transform<S, ServiceRequest> for AuthMiddleware
16where
17    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
18    S::Future: 'static,
19    B: 'static,
20{
21    type Response = ServiceResponse<EitherBody<B>>;
22    type Error = Error;
23    type InitError = ();
24    type Transform = AuthCheck<S>;
25    type Future = Ready<Result<Self::Transform, Self::InitError>>;
26
27    fn new_transform(&self, service: S) -> Self::Future {
28        ready(Ok(AuthCheck { service }))
29    }
30}
31
32/// Inner service wrapper created by [`AuthMiddleware`].
33pub struct AuthCheck<S> {
34    service: S,
35}
36
37impl<S, B> Service<ServiceRequest> for AuthCheck<S>
38where
39    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
40    S::Future: 'static,
41    B: 'static,
42{
43    type Response = ServiceResponse<EitherBody<B>>;
44    type Error = Error;
45    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
46
47    forward_ready!(service);
48
49    fn call(&self, req: ServiceRequest) -> Self::Future {
50        let session = req.get_session();
51
52        // Use cached session check (no DB query if user_data exists in session)
53        if !is_authenticated(&session) {
54            return Box::pin(async move {
55                Ok(req.into_response(
56                    HttpResponse::Found()
57                        .insert_header(("Location", "/signin"))
58                        .finish()
59                        .map_into_right_body(),
60                ))
61            });
62        }
63
64        let res: <S as Service<ServiceRequest>>::Future = self.service.call(req);
65        Box::pin(async move { res.await.map(ServiceResponse::map_into_left_body) })
66    }
67}