drogue_bazaar/actix/auth/authentication/
middleware.rs

1use super::{AuthN, Credentials, UsernameAndToken};
2use crate::auth::AuthError;
3use actix_http::body::EitherBody;
4use actix_service::{Service, Transform};
5use actix_web::{
6    dev::{ServiceRequest, ServiceResponse},
7    web::Query,
8    Error, HttpMessage,
9};
10use actix_web_httpauth::extractors::{basic::BasicAuth, bearer::BearerAuth};
11use chrono::{DateTime, Utc};
12use futures_util::future::{ok, LocalBoxFuture, Ready};
13use serde::Deserialize;
14use std::rc::Rc;
15
16pub struct AuthMiddleware<S> {
17    service: Rc<S>,
18    authenticator: AuthN,
19}
20
21// 1. Middleware initialization
22// Middleware factory is `Transform` trait from actix-service crate
23// `S` - type of the next service
24// `B` - type of response's body
25impl<S, B> Transform<S, ServiceRequest> for AuthN
26where
27    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
28    S::Future: 'static,
29    B: 'static,
30{
31    type Response = ServiceResponse<EitherBody<B>>;
32    type Error = S::Error;
33    type Transform = AuthMiddleware<S>;
34    type InitError = ();
35    type Future = Ready<Result<Self::Transform, Self::InitError>>;
36
37    fn new_transform(&self, service: S) -> Self::Future {
38        ok(AuthMiddleware {
39            service: Rc::new(service),
40            authenticator: self.clone(),
41        })
42    }
43}
44
45#[derive(Debug, Clone, PartialEq)]
46pub struct AuthenticatedUntil(pub DateTime<Utc>);
47
48#[derive(Deserialize, Debug)]
49struct Token {
50    token: String,
51}
52#[derive(Deserialize, Debug)]
53struct ApiKey {
54    username: String,
55    api_key: String,
56}
57
58// 2. Middleware's call method gets called with normal request.
59impl<S, B> Service<ServiceRequest> for AuthMiddleware<S>
60where
61    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
62    S::Future: 'static,
63    B: 'static,
64{
65    type Response = ServiceResponse<EitherBody<B>>;
66    type Error = Error;
67    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
68
69    actix_service::forward_ready!(service);
70
71    fn call(&self, mut req: ServiceRequest) -> Self::Future {
72        let srv = Rc::clone(&self.service);
73        let auth = self.authenticator.clone();
74
75        Box::pin(async move {
76            let basic_auth = req.extract::<BasicAuth>().await;
77            let bearer_auth = req.extract::<BearerAuth>().await;
78
79            // This match a "token" or "api_key" query parameter
80            let query_str = req.query_string();
81            let token_query_param = Query::<Token>::from_query(query_str);
82            let api_key_query_param = Query::<ApiKey>::from_query(query_str);
83
84            log::debug!(
85                "Basic: {:?}, Bearer: {:?}, Query.token: {:?} Query.api_key: {:?}",
86                basic_auth,
87                bearer_auth,
88                token_query_param,
89                api_key_query_param,
90            );
91
92            // now evaluate
93
94            let credentials = match (
95                basic_auth,
96                bearer_auth,
97                token_query_param,
98                api_key_query_param,
99            ) {
100                // basic auth is present
101                (Ok(basic), Err(_), Err(_), Err(_)) => {
102                    Ok(Credentials::AccessToken(UsernameAndToken {
103                        username: basic.user_id().to_string(),
104                        access_token: basic.password().map(|k| k.to_string()),
105                    }))
106                }
107                // bearer auth is present
108                (Err(_), Ok(bearer), Err(_), Err(_)) => {
109                    Ok(Credentials::OpenIDToken(bearer.token().to_string()))
110                }
111
112                // token query param is present
113                (Err(_), Err(_), Ok(query), Err(_)) => Ok(Credentials::OpenIDToken(query.0.token)),
114                // api_key query param is present
115                (Err(_), Err(_), Err(_), Ok(query)) => {
116                    Ok(Credentials::AccessToken(UsernameAndToken {
117                        username: query.0.username,
118                        access_token: Some(query.0.api_key),
119                    }))
120                }
121
122                // No headers and no query param (or both headers are invalid, but both invalid should be met with a Bad request anyway)
123                (Err(_), Err(_), Err(_query), Err(_api_key)) => Ok(Credentials::Anonymous),
124                // More than one way of authentication provided
125                // Note on both headers provided and valid -> This never happens, the NGINX load balancer sends back 400 Bad request.
126                (_, _, _, _) => Err(AuthError::InvalidRequest(
127                    "More than one way of authentication provided".to_string(),
128                )),
129            };
130
131            // authentication
132            let auth_result = match credentials {
133                Ok(c) => auth.authenticate(c).await,
134                Err(err) => {
135                    log::info!("Credentials error: {err}");
136                    Err(err)
137                }
138            };
139
140            match auth_result {
141                Ok((user, time)) => {
142                    log::debug!("Authenticated: {user:?}");
143                    // insert the UserInformation and the expiration time of the token in the request
144                    req.extensions_mut().insert(user);
145                    if let Some(exp) = time {
146                        req.extensions_mut().insert(AuthenticatedUntil(exp));
147                    }
148                    // then forward it to the next service
149                    srv.call(req).await.map(|res| res.map_into_left_body())
150                }
151                Err(err) => {
152                    log::debug!("Authentication error: {err}");
153                    Ok(req.error_response(err).map_into_right_body())
154                }
155            }
156        })
157    }
158}