headers/common/
authorization.rs

1//! Authorization header and types.
2
3use base64::engine::general_purpose::STANDARD as ENGINE;
4use base64::Engine;
5use bytes::Bytes;
6
7use util::HeaderValueString;
8use HeaderValue;
9
10/// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2)
11///
12/// The `Authorization` header field allows a user agent to authenticate
13/// itself with an origin server -- usually, but not necessarily, after
14/// receiving a 401 (Unauthorized) response.  Its value consists of
15/// credentials containing the authentication information of the user
16/// agent for the realm of the resource being requested.
17///
18/// # ABNF
19///
20/// ```text
21/// Authorization = credentials
22/// ```
23///
24/// # Example values
25/// * `Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==`
26/// * `Bearer fpKL54jvWmEGVoRdCNjG`
27///
28/// # Examples
29///
30/// ```
31/// # extern crate headers;
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_else(|| 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 super::super::{test_decode, test_encode};
216    use super::{Authorization, Basic, Bearer};
217    use http::header::HeaderMap;
218    use HeaderMapExt;
219
220    #[test]
221    fn basic_encode() {
222        let auth = Authorization::basic("Aladdin", "open sesame");
223        let headers = test_encode(auth);
224
225        assert_eq!(
226            headers["authorization"],
227            "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
228        );
229    }
230
231    #[test]
232    fn basic_roundtrip() {
233        let auth = Authorization::basic("Aladdin", "open sesame");
234        let mut h = HeaderMap::new();
235        h.typed_insert(auth.clone());
236        assert_eq!(h.typed_get(), Some(auth));
237    }
238
239    #[test]
240    fn basic_encode_no_password() {
241        let auth = Authorization::basic("Aladdin", "");
242        let headers = test_encode(auth);
243
244        assert_eq!(headers["authorization"], "Basic QWxhZGRpbjo=",);
245    }
246
247    #[test]
248    fn basic_decode() {
249        let auth: Authorization<Basic> =
250            test_decode(&["Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap();
251        assert_eq!(auth.0.username(), "Aladdin");
252        assert_eq!(auth.0.password(), "open sesame");
253    }
254
255    #[test]
256    fn basic_decode_case_insensitive() {
257        let auth: Authorization<Basic> =
258            test_decode(&["basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap();
259        assert_eq!(auth.0.username(), "Aladdin");
260        assert_eq!(auth.0.password(), "open sesame");
261    }
262
263    #[test]
264    fn basic_decode_extra_whitespaces() {
265        let auth: Authorization<Basic> =
266            test_decode(&["Basic  QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap();
267        assert_eq!(auth.0.username(), "Aladdin");
268        assert_eq!(auth.0.password(), "open sesame");
269    }
270
271    #[test]
272    fn basic_decode_no_password() {
273        let auth: Authorization<Basic> = test_decode(&["Basic QWxhZGRpbjo="]).unwrap();
274        assert_eq!(auth.0.username(), "Aladdin");
275        assert_eq!(auth.0.password(), "");
276    }
277
278    #[test]
279    fn bearer_encode() {
280        let auth = Authorization::bearer("fpKL54jvWmEGVoRdCNjG").unwrap();
281
282        let headers = test_encode(auth);
283
284        assert_eq!(headers["authorization"], "Bearer fpKL54jvWmEGVoRdCNjG",);
285    }
286
287    #[test]
288    fn bearer_decode() {
289        let auth: Authorization<Bearer> = test_decode(&["Bearer fpKL54jvWmEGVoRdCNjG"]).unwrap();
290        assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG");
291    }
292
293    #[test]
294    fn bearer_decode_case_insensitive() {
295        let auth: Authorization<Bearer> = test_decode(&["bearer fpKL54jvWmEGVoRdCNjG"]).unwrap();
296        assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG");
297    }
298
299    #[test]
300    fn bearer_decode_extra_whitespaces() {
301        let auth: Authorization<Bearer> = test_decode(&["Bearer   fpKL54jvWmEGVoRdCNjG"]).unwrap();
302        assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG");
303    }
304}
305
306//bench_header!(raw, Authorization<String>, { vec![b"foo bar baz".to_vec()] });
307//bench_header!(basic, Authorization<Basic>, { vec![b"Basic QWxhZGRpbjpuIHNlc2FtZQ==".to_vec()] });
308//bench_header!(bearer, Authorization<Bearer>, { vec![b"Bearer fpKL54jvWmEGVoRdCNjG".to_vec()] });