Skip to content

Commit

Permalink
Make authorization schemes case and whitespace insensitive (#153)
Browse files Browse the repository at this point in the history
* Make authorization schemes case and whitespace insensitive

According to RFC7235[1]:

> It uses a case-
> insensitive token as a means to identify the authentication scheme,
> followed by additional information necessary for achieving
> authentication via that scheme.

[1]: https://datatracker.ietf.org/doc/html/rfc7235#section-2.1

* Use eq_ignore_ascii_case() for Authorization schemes

This is effectively the same as comparing the result of
`to_ascii_lowercase()`, with the benefit of avoiding "allocating and
copying temporaries" (according to the Rust `std` docs[1]).

[1]: https://doc.rust-lang.org/std/primitive.slice.html#method.eq_ignore_ascii_case

---------

Co-authored-by: Simon Bihel <simon.bihel@spruceid.com>
  • Loading branch information
cole-h and sbihel authored Nov 20, 2023
1 parent 4400aa9 commit 7d784cd
Showing 1 changed file with 33 additions and 5 deletions.
38 changes: 33 additions & 5 deletions src/common/authorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ impl<C: Credentials> ::Header for Authorization<C> {
.next()
.and_then(|val| {
let slice = val.as_bytes();
if slice.starts_with(C::SCHEME.as_bytes())
&& slice.len() > C::SCHEME.len()
if slice.len() > C::SCHEME.len()
&& slice[C::SCHEME.len()] == b' '
&& slice[..C::SCHEME.len()].eq_ignore_ascii_case(C::SCHEME.as_bytes())
{
C::decode(val).map(Authorization)
} else {
Expand Down Expand Up @@ -151,7 +151,7 @@ impl Credentials for Basic {

fn decode(value: &HeaderValue) -> Option<Self> {
debug_assert!(
value.as_bytes().starts_with(b"Basic "),
value.as_bytes()[..Self::SCHEME.len()].eq_ignore_ascii_case(Self::SCHEME.as_bytes()),
"HeaderValue to decode should start with \"Basic ..\", received = {:?}",
value,
);
Expand Down Expand Up @@ -186,7 +186,7 @@ pub struct Bearer(HeaderValueString);
impl Bearer {
/// View the token part as a `&str`.
pub fn token(&self) -> &str {
&self.0.as_str()["Bearer ".len()..]
self.0.as_str()["Bearer ".len()..].trim_start()
}
}

Expand All @@ -195,7 +195,7 @@ impl Credentials for Bearer {

fn decode(value: &HeaderValue) -> Option<Self> {
debug_assert!(
value.as_bytes().starts_with(b"Bearer "),
value.as_bytes()[..Self::SCHEME.len()].eq_ignore_ascii_case(Self::SCHEME.as_bytes()),
"HeaderValue to decode should start with \"Bearer ..\", received = {:?}",
value,
);
Expand Down Expand Up @@ -252,6 +252,22 @@ mod tests {
assert_eq!(auth.0.password(), "open sesame");
}

#[test]
fn basic_decode_case_insensitive() {
let auth: Authorization<Basic> =
test_decode(&["basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap();
assert_eq!(auth.0.username(), "Aladdin");
assert_eq!(auth.0.password(), "open sesame");
}

#[test]
fn basic_decode_extra_whitespaces() {
let auth: Authorization<Basic> =
test_decode(&["Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap();
assert_eq!(auth.0.username(), "Aladdin");
assert_eq!(auth.0.password(), "open sesame");
}

#[test]
fn basic_decode_no_password() {
let auth: Authorization<Basic> = test_decode(&["Basic QWxhZGRpbjo="]).unwrap();
Expand All @@ -273,6 +289,18 @@ mod tests {
let auth: Authorization<Bearer> = test_decode(&["Bearer fpKL54jvWmEGVoRdCNjG"]).unwrap();
assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG");
}

#[test]
fn bearer_decode_case_insensitive() {
let auth: Authorization<Bearer> = test_decode(&["bearer fpKL54jvWmEGVoRdCNjG"]).unwrap();
assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG");
}

#[test]
fn bearer_decode_extra_whitespaces() {
let auth: Authorization<Bearer> = test_decode(&["Bearer fpKL54jvWmEGVoRdCNjG"]).unwrap();
assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG");
}
}

//bench_header!(raw, Authorization<String>, { vec![b"foo bar baz".to_vec()] });
Expand Down

0 comments on commit 7d784cd

Please sign in to comment.