views:

214

answers:

2

Various articles (1, 2) I discovered make this look easy enough:

WebRequest request = HttpWebRequest.Create(url);

var credentialCache = new CredentialCache();
credentialCache.Add(
  new Uri(url), // request url
  "Digest", // authentication type
  new NetworkCredential("user", "password") // credentials
);

request.Credentials = credentialCache;

However, this only works for URLs without URL parameters. For example, I can download http://example.com/test/xyz.html just fine, but when I attempt to download http://example.com/test?page=xyz, the result is a 400 Bad Request message with the following in the server's logs (running Apache 2.2):

Digest: uri mismatch - </test> does not match request-uri </test?page=xyz>

My first idea was that the digest specification requires URL parameters to be removed from the digest hash -- but removing the parameter from the URL passed to credentialCache.Add() didn't change a thing. So it must be the other way around and somewhere in the .NET framework is wrongly removing the parameter from the URL.

A: 

I think the second URL points to dynamic page and you should first call it using GET to get the HTML and then to download it. No experience in this field though.

Umair Ashraf
Sorry, no. It's completely up to the web server what to do with the URL and the first page could be dynamic as well. Further, the HTML is what is downloaded, there's no difference between downloading HTML or downloading something else.
Cygon
+1  A: 

You said you removed the querystring paramters, but did you try going all the way back to just the host? Every single example of CredentialsCache.Add() I've seen seems to use only the host, and the docs for CredentialsCache.Add() list the Uri parameter as "uriPrefix", which seems telling.

In other words, try this out:

Uri uri = new Uri(url);
WebRequest request = WebRequest.Create(uri);

var credentialCache = new CredentialCache(); 
credentialCache.Add( 
  new Uri(uri.GetLeftPart(UriPartial.Authority)), // request url's host
  "Digest",  // authentication type 
  new NetworkCredential("user", "password") // credentials 
); 

request.Credentials = credentialCache;

If this works, you will also have to make sure that you don't add the same "authority" to the cache more than once... all requests to the same host should be able to make use of the same credential cache entry.

JaredReisinger
Strange, I didn't come across a single example using just the root URI for authentication. In any case, it doesn't work, sorry. As per section 3.2.2 of RFC 2617 (http://rfc.askapache.com/rfc2617/rfc2617.html#section-3.2.2) the digest URI should be identical to the 'request-uri' in the HTTP request.
Cygon
Here are some examples: http://msdn.microsoft.com/en-us/library/system.net.credentialcache.aspx, http://support.microsoft.com/kb/822456, http://blogs.msdn.com/b/buckh/archive/2004/07/28/199706.aspx (though admittedly it's a 'localhost' example).
JaredReisinger
Yes, the RFC says that the digest-uri should match the request, but that's what is sent on the wire, not what's stored in the cache. The CredentialCache.GetCredential() doc (http://msdn.microsoft.com/en-us/library/fy4394xd.aspx) says that "GetCredential uses the longest matching URI prefix in the cache to determine which set of credentials to return for an authorization type." It then shows that passing a domain will cause the credentials to get used for *all* resources under that domain.
JaredReisinger
I see, you're right, the 'digest-uri' sent to the server for authentication of a single request has no relation to the URI stored in the credential cache. So in production code, one can store a higher level URI like `http://example.com/restrictedarea/` in the credential cache and reuse the cache for all URIs below that path.
Cygon
Anyway, this doesn't affect the problem. The credentials are successfully looked up in the cache (if that's your worry) and the HttpWebRequest does attempt to authenticate against my web server (as can be seen in the server logs) - but with the wrong 'digest-uri' whenever the requested URL has parameters appended to it.
Cygon
Ah hah... sounds like a bug in the HttpWebRequest credentials code. So it is really the Microsoft Connect bug mentioned in the comments on your question (https://connect.microsoft.com/VisualStudio/feedback/details/571052/digest-authentication-does-not-send-the-full-uri-path-in-the-uri-parameter). That sucks.
JaredReisinger