maoertel / diqwest

Crate to extend `reqwest` to be able to send with digest auth flow.
https://docs.rs/diqwest
MIT License
18 stars 3 forks source link

Bad requests due to full request URI in Authorization header #1

Closed edmonds closed 2 years ago

edmonds commented 2 years ago

Hi,

I was not able to get diqwest to successfully work against an embedded device with an HTTP server that requires digest authentication. I was able to get it to work with curl --digest, so I compared the requests generated by curl vs. those generated by reqwest+diqwest.

It turns out curl generates an Authorization header that looks like:

Authorization: Digest username="...", realm="...", nonce="...", uri="/cgi-bin/egauge-show?c&T=1639976400,1642654800", cnonce="...", nc=00000001, qop=auth, response="..."

while reqwest+diqwest generates an Authorization header that looks like:

authorization: Digest username="...", realm="...", nonce="...", uri="http://100.100.0.208/cgi-bin/egauge-show?c&T=1639976400,1642654800", qop=auth, nc=00000001, cnonce="...", response="...", algorithm=MD5

I was able to get diqwest to generate an Authorization header closer to what curl generates with something like this:

diff --git a/src/core.rs b/src/core.rs
index 14d714a..2c2e9f5 100644
--- a/src/core.rs
+++ b/src/core.rs
@@ -25,11 +25,24 @@ impl WithDigestAuth for RequestBuilder {
     match first_response.status() {
       StatusCode::UNAUTHORIZED => {
         let request = clone_request_builder(self)?.build()?;
-        let url = request.url();
+        let full_url = request.url();
         let method = HttpMethod::from(request.method().as_str());
         let body = request.body().and_then(|b| b.as_bytes());
-        let answer =
-          DigestAuthHelper::parse_digest_auth_header(first_response, url.as_str(), method, body, username, password)?;
+
+        let mut base_url = full_url.clone();
+        match base_url.path_segments_mut() {
+          Ok(mut path) => {
+            path.clear();
+          }
+          Err(_) => {}
+        }
+
+        let url = format!(
+          "/{}",
+          base_url.make_relative(&full_url).ok_or(RequestBuilderNotCloneableError)?
+        );
+
+        let answer = DigestAuthHelper::parse_digest_auth_header(first_response, &url, method, body, username, password)?;

         Ok(clone_request_builder(self)?.header("Authorization", answer).send().await?)
       }

Is this a bug in diqwest?

Thanks!

maoertel commented 2 years ago

Hi @edmonds,

something is off with my notifications. I did not get a message when you opened this issue (3 months ago 🤦🏾‍♀️). Just stumbled upon it by accident. So sorry for my late reply.

I was looking into your issue and it seems you are totally right. First I looked it up in the ietf rfc7616 where I read the Effective Request URI (see here) differently. But then I googled some examples and looked into the implementation of the underlying crate digest_auth and saw this. It clearly states that it is without domain and should start with a slash.

Took care here and already published to crates.io.

Thanks for reporting it! 👋🏾