ttpkit_auth/
basic.rs

1//! HTTP Basic authentication.
2
3use std::{
4    fmt::{self, Display, Formatter},
5    str::FromStr,
6};
7
8use base64::{Engine, display::Base64Display, prelude::BASE64_STANDARD};
9use ttpkit::header::HeaderFieldValue;
10
11use crate::Error;
12
13/// Basic authorization header.
14#[derive(Clone)]
15pub struct BasicAuth {
16    username: String,
17    password: String,
18}
19
20impl BasicAuth {
21    /// Create a new authorization header.
22    pub fn new<U, P>(username: U, password: P) -> Self
23    where
24        U: Into<String>,
25        P: Into<String>,
26    {
27        Self {
28            username: username.into(),
29            password: password.into(),
30        }
31    }
32
33    /// Get the username.
34    #[inline]
35    pub fn username(&self) -> &str {
36        &self.username
37    }
38
39    /// Get the password.
40    #[inline]
41    pub fn password(&self) -> &str {
42        &self.password
43    }
44}
45
46impl Display for BasicAuth {
47    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
48        let credentials = format!("{}:{}", self.username, self.password);
49
50        let b64c = Base64Display::new(credentials.as_bytes(), &BASE64_STANDARD);
51
52        write!(f, "Basic {b64c}")
53    }
54}
55
56impl From<BasicAuth> for HeaderFieldValue {
57    fn from(auth: BasicAuth) -> Self {
58        HeaderFieldValue::from(auth.to_string())
59    }
60}
61
62impl FromStr for BasicAuth {
63    type Err = Error;
64
65    fn from_str(s: &str) -> Result<Self, Self::Err> {
66        let (scheme, rest) = s
67            .trim_ascii_start()
68            .split_once(|c| char::is_ascii_whitespace(&c))
69            .ok_or_else(|| Error::from_static_msg("invalid basic authorization header"))?;
70
71        if !scheme.eq_ignore_ascii_case("basic") {
72            return Err(Error::from_static_msg("unexpected authorization scheme"));
73        }
74
75        let credentials = BASE64_STANDARD
76            .decode(rest.trim_ascii())
77            .map_err(|_| Error::from_static_msg("invalid basic authorization credentials"))?;
78
79        let (username, password) = str::from_utf8(&credentials)
80            .ok()
81            .and_then(|credentials| credentials.split_once(':'))
82            .ok_or_else(|| Error::from_static_msg("invalid basic authorization credentials"))?;
83
84        let res = Self {
85            username: username.into(),
86            password: password.into(),
87        };
88
89        Ok(res)
90    }
91}