headers/common/
authorization.rs

1//! Authorization header and types.
2
3use base64::engine::general_purpose::STANDARD as ENGINE;
4use base64::Engine;
5use bytes::Bytes;
6use http::{HeaderName, HeaderValue};
7
8use crate::util::HeaderValueString;
9use crate::{Error, Header};
10
11/// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2)
12///
13/// The `Authorization` header field allows a user agent to authenticate
14/// itself with an origin server -- usually, but not necessarily, after
15/// receiving a 401 (Unauthorized) response.  Its value consists of
16/// credentials containing the authentication information of the user
17/// agent for the realm of the resource being requested.
18///
19/// # ABNF
20///
21/// ```text
22/// Authorization = credentials
23/// ```
24///
25/// # Example values
26/// * `Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==`
27/// * `Bearer fpKL54jvWmEGVoRdCNjG`
28///
29/// # Examples
30///
31/// ```
32/// use headers::Authorization;
33///
34/// let basic = Authorization::basic("Aladdin", "open sesame");
35/// let bearer = Authorization::bearer("some-opaque-token").unwrap();
36/// ```
37///
38#[derive(Clone, PartialEq, Debug)]
39pub struct Authorization<C: Credentials>(pub C);
40
41impl Authorization<Basic> {
42    /// Create a `Basic` authorization header.
43    pub fn basic(username: &str, password: &str) -> Self {
44        let colon_pos = username.len();
45        let decoded = format!("{}:{}", username, password);
46
47        Authorization(Basic { decoded, colon_pos })
48    }
49
50    /// View the decoded username.
51    pub fn username(&self) -> &str {
52        self.0.username()
53    }
54
55    /// View the decoded password.
56    pub fn password(&self) -> &str {
57        self.0.password()
58    }
59}
60
61impl Authorization<Bearer> {
62    /// Try to create a `Bearer` authorization header.
63    pub fn bearer(token: &str) -> Result<Self, InvalidBearerToken> {
64        HeaderValueString::from_string(format!("Bearer {}", token))
65            .map(|val| Authorization(Bearer(val)))
66            .ok_or(InvalidBearerToken { _inner: () })
67    }
68
69    /// View the token part as a `&str`.
70    pub fn token(&self) -> &str {
71        self.0.token()
72    }
73}
74
75impl<C: Credentials> Header for Authorization<C> {
76    fn name() -> &'static HeaderName {
77        &::http::header::AUTHORIZATION
78    }
79
80    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, Error> {
81        values
82            .next()
83            .and_then(|val| {
84                let slice = val.as_bytes();
85                if slice.len() > C::SCHEME.len()
86                    && slice[C::SCHEME.len()] == b' '
87                    && slice[..C::SCHEME.len()].eq_ignore_ascii_case(C::SCHEME.as_bytes())
88                {
89                    C::decode(val).map(Authorization)
90                } else {
91                    None
92                }
93            })
94            .ok_or_else(Error::invalid)
95    }
96
97    fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
98        let mut value = self.0.encode();
99        value.set_sensitive(true);
100        debug_assert!(
101            value.as_bytes().starts_with(C::SCHEME.as_bytes()),
102            "Credentials::encode should include its scheme: scheme = {:?}, encoded = {:?}",
103            C::SCHEME,
104            value,
105        );
106
107        values.extend(::std::iter::once(value));
108    }
109}
110
111/// Credentials to be used in the `Authorization` header.
112pub trait Credentials: Sized {
113    /// The scheme identify the format of these credentials.
114    ///
115    /// This is the static string that always prefixes the actual credentials,
116    /// like `"Basic"` in basic authorization.
117    const SCHEME: &'static str;
118
119    /// Try to decode the credentials from the `HeaderValue`.
120    ///
121    /// The `SCHEME` will be the first part of the `value`.
122    fn decode(value: &HeaderValue) -> Option<Self>;
123
124    /// Encode the credentials to a `HeaderValue`.
125    ///
126    /// The `SCHEME` must be the first part of the `value`.
127    fn encode(&self) -> HeaderValue;
128}
129
130/// Credential holder for Basic Authentication
131#[derive(Clone, PartialEq, Debug)]
132pub struct Basic {
133    decoded: String,
134    colon_pos: usize,
135}
136
137impl Basic {
138    /// View the decoded username.
139    pub fn username(&self) -> &str {
140        &self.decoded[..self.colon_pos]
141    }
142
143    /// View the decoded password.
144    pub fn password(&self) -> &str {
145        &self.decoded[self.colon_pos + 1..]
146    }
147}
148
149impl Credentials for Basic {
150    const SCHEME: &'static str = "Basic";
151
152    fn decode(value: &HeaderValue) -> Option<Self> {
153        debug_assert!(
154            value.as_bytes()[..Self::SCHEME.len()].eq_ignore_ascii_case(Self::SCHEME.as_bytes()),
155            "HeaderValue to decode should start with \"Basic ..\", received = {:?}",
156            value,
157        );
158
159        let bytes = &value.as_bytes()["Basic ".len()..];
160        let non_space_pos = bytes.iter().position(|b| *b != b' ')?;
161        let bytes = &bytes[non_space_pos..];
162
163        let bytes = ENGINE.decode(bytes).ok()?;
164
165        let decoded = String::from_utf8(bytes).ok()?;
166
167        let colon_pos = decoded.find(':')?;
168
169        Some(Basic { decoded, colon_pos })
170    }
171
172    fn encode(&self) -> HeaderValue {
173        let mut encoded = String::from("Basic ");
174        ENGINE.encode_string(&self.decoded, &mut encoded);
175
176        let bytes = Bytes::from(encoded);
177        HeaderValue::from_maybe_shared(bytes)
178            .expect("base64 encoding is always a valid HeaderValue")
179    }
180}
181
182#[derive(Clone, PartialEq, Debug)]
183/// Token holder for Bearer Authentication, most often seen with oauth
184pub struct Bearer(HeaderValueString);
185
186impl Bearer {
187    /// View the token part as a `&str`.
188    pub fn token(&self) -> &str {
189        self.0.as_str()["Bearer ".len()..].trim_start()
190    }
191}
192
193impl Credentials for Bearer {
194    const SCHEME: &'static str = "Bearer";
195
196    fn decode(value: &HeaderValue) -> Option<Self> {
197        debug_assert!(
198            value.as_bytes()[..Self::SCHEME.len()].eq_ignore_ascii_case(Self::SCHEME.as_bytes()),
199            "HeaderValue to decode should start with \"Bearer ..\", received = {:?}",
200            value,
201        );
202
203        HeaderValueString::from_val(value).ok().map(Bearer)
204    }
205
206    fn encode(&self) -> HeaderValue {
207        (&self.0).into()
208    }
209}
210
211error_type!(InvalidBearerToken);
212
213#[cfg(test)]
214mod tests {
215    use http::header::HeaderMap;
216
217    use super::super::{test_decode, test_encode};
218    use super::{Authorization, Basic, Bearer};
219    use crate::HeaderMapExt;
220
221    #[test]
222    fn basic_encode() {
223        let auth = Authorization::basic("Aladdin", "open sesame");
224        let headers = test_encode(auth);
225
226        assert_eq!(
227            headers["authorization"],
228            "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
229        );
230    }
231
232    #[test]
233    fn basic_roundtrip() {
234        let auth = Authorization::basic("Aladdin", "open sesame");
235        let mut h = HeaderMap::new();
236        h.typed_insert(auth.clone());
237        assert_eq!(h.typed_get(), Some(auth));
238    }
239
240    #[test]
241    fn basic_encode_no_password() {
242        let auth = Authorization::basic("Aladdin", "");
243        let headers = test_encode(auth);
244
245        assert_eq!(headers["authorization"], "Basic QWxhZGRpbjo=",);
246    }
247
248    #[test]
249    fn basic_decode() {
250        let auth: Authorization<Basic> =
251            test_decode(&["Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap();
252        assert_eq!(auth.0.username(), "Aladdin");
253        assert_eq!(auth.0.password(), "open sesame");
254    }
255
256    #[test]
257    fn basic_decode_case_insensitive() {
258        let auth: Authorization<Basic> =
259            test_decode(&["basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap();
260        assert_eq!(auth.0.username(), "Aladdin");
261        assert_eq!(auth.0.password(), "open sesame");
262    }
263
264    #[test]
265    fn basic_decode_extra_whitespaces() {
266        let auth: Authorization<Basic> =
267            test_decode(&["Basic  QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap();
268        assert_eq!(auth.0.username(), "Aladdin");
269        assert_eq!(auth.0.password(), "open sesame");
270    }
271
272    #[test]
273    fn basic_decode_no_password() {
274        let auth: Authorization<Basic> = test_decode(&["Basic QWxhZGRpbjo="]).unwrap();
275        assert_eq!(auth.0.username(), "Aladdin");
276        assert_eq!(auth.0.password(), "");
277    }
278
279    #[test]
280    fn bearer_encode() {
281        let auth = Authorization::bearer("fpKL54jvWmEGVoRdCNjG").unwrap();
282
283        let headers = test_encode(auth);
284
285        assert_eq!(headers["authorization"], "Bearer fpKL54jvWmEGVoRdCNjG",);
286    }
287
288    #[test]
289    fn bearer_decode() {
290        let auth: Authorization<Bearer> = test_decode(&["Bearer fpKL54jvWmEGVoRdCNjG"]).unwrap();
291        assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG");
292    }
293
294    #[test]
295    fn bearer_decode_case_insensitive() {
296        let auth: Authorization<Bearer> = test_decode(&["bearer fpKL54jvWmEGVoRdCNjG"]).unwrap();
297        assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG");
298    }
299
300    #[test]
301    fn bearer_decode_extra_whitespaces() {
302        let auth: Authorization<Bearer> = test_decode(&["Bearer   fpKL54jvWmEGVoRdCNjG"]).unwrap();
303        assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG");
304    }
305}
306
307//bench_header!(raw, Authorization<String>, { vec![b"foo bar baz".to_vec()] });
308//bench_header!(basic, Authorization<Basic>, { vec![b"Basic QWxhZGRpbjpuIHNlc2FtZQ==".to_vec()] });
309//bench_header!(bearer, Authorization<Bearer>, { vec![b"Bearer fpKL54jvWmEGVoRdCNjG".to_vec()] });