actix_web_security/authentication/
middleware.rs

1//! The middleware module provides the http authentication middleware and authentication service.
2
3use std::cell::RefCell;
4use std::future::{self, Future, Ready};
5use std::pin::Pin;
6use std::rc::Rc;
7use std::sync::Arc;
8use std::task::{Context, Poll};
9
10use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
11use actix_web::Error;
12
13use crate::authentication::endpoint_matcher::EndpointMatcher;
14use crate::authentication::error::error_type::AuthenticationError;
15use crate::authentication::scheme::header_extractor::AuthorizationHeaderExtractor;
16use crate::authentication::ProviderManager;
17use crate::user_details::attachment::UserDetailsRequestAttachmentHelper;
18
19/// The `HttpAuthenticationMiddleware` is an actix middleware that wraps client requets, initiates
20/// and orchestrates the authentication process.  
21/// A `HttpAuthenticationMiddleware`is specific for the type of the authorization header extraction
22/// and a set of endpoints.
23pub struct HttpAuthenticationMiddleware<T, U>
24where
25    T: AuthorizationHeaderExtractor + Clone,
26    U: EndpointMatcher + Clone,
27{
28    authorization_extractor: Box<T>,
29    provider_manager: ProviderManager,
30    endpoint_matcher: Box<U>,
31}
32
33impl<T: AuthorizationHeaderExtractor + Clone, U: EndpointMatcher + Clone>
34    HttpAuthenticationMiddleware<T, U>
35{
36    /// Constructs a new instance of `HttpAuthenticationMiddleware` for a given
37    /// `ProviderManager`, a boxed `AuthorizationHeaderExtractor` and a boxed
38    /// `EndpointMatcher`.
39    pub fn new(
40        provider_manager: ProviderManager,
41        authorization_extractor: Box<T>,
42        endpoint_matcher: Box<U>,
43    ) -> HttpAuthenticationMiddleware<T, U> {
44        HttpAuthenticationMiddleware {
45            authorization_extractor,
46            provider_manager,
47            endpoint_matcher,
48        }
49    }
50}
51
52impl<S, B, T, U> Transform<S> for HttpAuthenticationMiddleware<T, U>
53where
54    T: AuthorizationHeaderExtractor + Clone + 'static,
55    U: EndpointMatcher + Clone,
56    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
57{
58    type Request = ServiceRequest;
59    type Response = ServiceResponse<B>;
60    type Error = Error;
61    type Transform = HttpAuthenticationService<S, T, U>;
62    type InitError = ();
63    type Future = Ready<Result<Self::Transform, Self::InitError>>;
64
65    fn new_transform(&self, service: S) -> Self::Future {
66        let service = Rc::new(RefCell::new(service));
67        let provider_manager = Arc::new(self.provider_manager.clone());
68        let authorization_extractor = Arc::new(self.authorization_extractor.clone());
69        let endpoint_matcher = Arc::new(self.endpoint_matcher.clone());
70        future::ready(Ok(HttpAuthenticationService {
71            service,
72            provider_manager,
73            authorization_extractor,
74            endpoint_matcher,
75        }))
76    }
77}
78
79/// The `HttpAuthenticationService` executes the authentication process (header extraction, authentication, error handling).
80pub struct HttpAuthenticationService<
81    S,
82    T: AuthorizationHeaderExtractor + Clone,
83    U: EndpointMatcher + Clone,
84> {
85    service: Rc<RefCell<S>>,
86    provider_manager: Arc<ProviderManager>,
87    authorization_extractor: Arc<Box<T>>,
88    endpoint_matcher: Arc<Box<U>>,
89}
90
91impl<S, B, T, U> Service for HttpAuthenticationService<S, T, U>
92where
93    U: EndpointMatcher + Clone,
94    T: AuthorizationHeaderExtractor + Clone + 'static,
95    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
96{
97    type Request = ServiceRequest;
98    type Response = ServiceResponse<B>;
99    type Error = Error;
100    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Error>>>>;
101
102    fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
103        self.service.poll_ready(cx)
104    }
105
106    fn call(&mut self, req: Self::Request) -> Self::Future {
107        let service = Rc::clone(&self.service);
108        let handle_request = self.endpoint_matcher.do_match(&req);
109
110        if handle_request {
111            let authorization_extractor = Arc::clone(&self.authorization_extractor);
112            let provider_manager = Arc::clone(&self.provider_manager);
113            Box::pin(async move {
114                let error: Option<AuthenticationError>;
115
116                let extracted_token = authorization_extractor.extract_token(&req.headers()).await;
117                match extracted_token {
118                    Ok(token) => {
119                        let authentication_result = provider_manager.authenticate(&token).await;
120                        match authentication_result {
121                            Ok(result) => {
122                                req.attach(result);
123                                error = None;
124                            }
125                            Err(e) => error = Some(e),
126                        };
127                    }
128                    Err(e) => error = Some(e),
129                };
130
131                match error {
132                    Some(e) => Err(e.into()),
133                    None => {
134                        let fut = service.borrow_mut().call(req);
135                        let res = fut.await?;
136
137                        Ok(res)
138                    }
139                }
140            })
141        } else {
142            Box::pin(async move {
143                let fut = service.borrow_mut().call(req);
144                let res = fut.await?;
145
146                Ok(res)
147            })
148        }
149    }
150}