actix_web_httpauth/extractors/
basic.rs

1//! Extractor for the "Basic" HTTP Authentication Scheme.
2
3use std::borrow::Cow;
4
5use actix_utils::future::{ready, Ready};
6use actix_web::{dev::Payload, http::header::Header, FromRequest, HttpRequest};
7
8use super::{config::AuthExtractorConfig, errors::AuthenticationError};
9use crate::headers::{
10    authorization::{Authorization, Basic},
11    www_authenticate::basic::Basic as Challenge,
12};
13
14/// [`BasicAuth`] extractor configuration used for [`WWW-Authenticate`] header later.
15///
16/// [`WWW-Authenticate`]: crate::headers::www_authenticate::WwwAuthenticate
17#[derive(Debug, Clone, Default)]
18pub struct Config(Challenge);
19
20impl Config {
21    /// Set challenge `realm` attribute.
22    ///
23    /// The "realm" attribute indicates the scope of protection in the manner described in HTTP/1.1
24    /// [RFC 2617 ยง1.2](https://tools.ietf.org/html/rfc2617#section-1.2).
25    pub fn realm<T>(mut self, value: T) -> Config
26    where
27        T: Into<Cow<'static, str>>,
28    {
29        self.0.realm = Some(value.into());
30        self
31    }
32}
33
34impl AsRef<Challenge> for Config {
35    fn as_ref(&self) -> &Challenge {
36        &self.0
37    }
38}
39
40impl AuthExtractorConfig for Config {
41    type Inner = Challenge;
42
43    fn into_inner(self) -> Self::Inner {
44        self.0
45    }
46}
47
48/// Extractor for HTTP Basic auth.
49///
50/// # Examples
51/// ```
52/// use actix_web_httpauth::extractors::basic::BasicAuth;
53///
54/// async fn index(auth: BasicAuth) -> String {
55///     format!("Hello, {}!", auth.user_id())
56/// }
57/// ```
58///
59/// If authentication fails, this extractor fetches the [`Config`] instance from the [app data] in
60/// order to properly form the `WWW-Authenticate` response header.
61///
62/// # Examples
63/// ```
64/// use actix_web::{web, App};
65/// use actix_web_httpauth::extractors::basic::{self, BasicAuth};
66///
67/// async fn index(auth: BasicAuth) -> String {
68///     format!("Hello, {}!", auth.user_id())
69/// }
70///
71/// App::new()
72///     .app_data(basic::Config::default().realm("Restricted area"))
73///     .service(web::resource("/index.html").route(web::get().to(index)));
74/// ```
75///
76/// [app data]: https://docs.rs/actix-web/4/actix_web/struct.App.html#method.app_data
77#[derive(Debug, Clone)]
78pub struct BasicAuth(Basic);
79
80impl BasicAuth {
81    /// Returns client's user-ID.
82    pub fn user_id(&self) -> &str {
83        self.0.user_id()
84    }
85
86    /// Returns client's password.
87    pub fn password(&self) -> Option<&str> {
88        self.0.password()
89    }
90}
91
92impl From<Basic> for BasicAuth {
93    fn from(basic: Basic) -> Self {
94        Self(basic)
95    }
96}
97
98impl FromRequest for BasicAuth {
99    type Future = Ready<Result<Self, Self::Error>>;
100    type Error = AuthenticationError<Challenge>;
101
102    fn from_request(req: &HttpRequest, _: &mut Payload) -> <Self as FromRequest>::Future {
103        ready(
104            Authorization::<Basic>::parse(req)
105                .map(|auth| BasicAuth(auth.into_scheme()))
106                .map_err(|err| {
107                    log::debug!("`BasicAuth` extract error: {}", err);
108
109                    let challenge = req
110                        .app_data::<Config>()
111                        .map(|config| config.0.clone())
112                        .unwrap_or_default();
113
114                    AuthenticationError::new(challenge)
115                }),
116        )
117    }
118}