none
Setting DefaultCredentials with HttpClient?

    Question

  • Hi all,

    Can someone explain how to set Credentials on the new HttpClient object?

    In the older .NET model we used code like the following:

    HttpWebRequest request   = (HttpWebRequest)WebRequest.Create(Uri);
    request.Credentials      = System.Net.CredentialCache.DefaultNetworkCredentials;
    HttpWebResponse response = (HttpWebResponse) request.GetResponse();

    However with Metro apps and .NET 4.5 it is recommended to use HttpClient objects since they are more robust.

    I have some simple code like the following:

    try
    {
       HttpClient client = new HttpClient( );
       client.MaxResponseContentBufferSize = 10000000;
       string responseString = await client.GetStringAsync(Uri);
    
       searchResults.Text = responseString;
    }
    catch (HttpRequestException e)
    {
        searchResults.Text = e.Message;
    }

    Which i'm using for testing and just sets the raw XML or JSON from which ever web service I pass as the Uri as the Text property of the searchResults TextBox.

    However, some of the services require authentication and I'm not sure how to set those using the new HttpClient object.
    For instance, if I send a query to the Bing Image service which uses a developer key I get the JSON back correctly. If I try to access my REST service it says "Response status code does not indicate success: 401 (Unauthorized)." even though if I submit the same query through a web browser, say Internet Explorer, the service correctly returns XML.

    Based on the conditions that I know my Metro program works for Bing Image Search API, and I know my REST service works through Internet explorer I've concluded that it must be a security credentials issue from Metro app becaus they do not default to the current user.

    I reviewed the //build video on HTTP Services which set the AuthorizationHeaders as shown below:

    client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new Authorizaiton( "OAuth", accessToken );

    But this code was using 'accessToken' parsed from a Facebook URL.  I'm looking to just re-use something like:
    request.Credentials   = System.Net.CredentialCache.DefaultNetworkCredentials;

    only with the new HttpClient model.

    Thanks,
    - Matt

    Wednesday, May 16, 2012 8:54 PM

Answers

  • SO, this is a lame ending, but it turns out it was just a matter of selecting another checkbox in the Package.appmanifest.  I didn't realize that the Private Networks ( Client & Server ) capability did not include Domain account credentials.  This whole time all I needed to do was enable the Enterprise Authentication. :(

    At least it's fixed, hope you all don't make the same mistake I have.  Be sure to check all the capabilities before you waste hours searching through the Object Browser!

    Thursday, May 17, 2012 4:55 PM

All replies

  • Update:

    Just to be clear:

    In my package.appmanifest - capabilities I have the following enabled:

    Internet (Client)

    Private Networks ( Client & Server ) - I need this one because my REST service is on an internal domain

     

    So I just found this post:

    http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/78c981f4-9dc6-4785-a8ff-f8e90d7c93b7

    Which suggests code as the following:

    HttpClientHandler handler = new HttpClientHandler();
    handler.UseDefaultCredentials = true;
    HttpClient client = new HttpClient(handler);
    

    I have tried this in my example and after rebuilding and running I get a non-descript error "An error occurred while sending the request." from the HttpRequestException e.

    From stepping through the code the error was occuring on line:

    string responseString = await client.GetStringAsync(Uri);
     

    so I replaced that with the more verbose calls:

    HttpResponseMessage response = await client.GetAsync( Uri );
    response.EnsureSuccessStatusCode();
    string responseString = await response.Content.ReadAsStringAsync();

    and there is still an error on the GetAsync call which I cannot step into.

    - Matt

     

     

    Wednesday, May 16, 2012 9:15 PM
  • Update again:

    So after more investigation I found that the HttpRequestError was actually holding more information than expected:

    Here is the full list:

    -  e {"An error occurred while sending the request."} System.Net.Http.HttpRequestException
    -  InnerException {"The remote server returned an error: (401) Unauthorized."} System.Exception {System.Net.WebException}
    -  InnerException {"No credentials are available in the security package"} System.Exception {System.ComponentModel.Win32Exception}

    From this, we can see that it is still a security/credentials issue.  So the handler.UseDefaultCredentials did not seem to help.

    Wednesday, May 16, 2012 9:26 PM
  • It seems there are only 3 options for setting credentials.  I'm going to list all of them and explain why I think 2 of them we have already eliminated from possible solutions and hope that someone can confirm.

    Option 1: Set Credentials on the HttpWebRequest

    HttpWebRequest request   = (HttpWebRequest)WebRequest.Create(Uri);
    request.Credentials      = System.Net.CredentialCache.DefaultNetworkCredentials;

    Eliminated: .NET 4.5 no longer has an HttpWebRequest object.  It uses HttpClient objects which have different methods of setting credentials.

    Option 2: Set Credentials in the HttpClientHandler

    HttpClientHandler handler = new HttpClientHandler();
    handler.UseDefaultCredentials = true;
    HttpClient client = new HttpClient(handler);


    Eliminated: I'm not sure why this doesn't work, but the only thing it changed in my results was add another exception layer.

    Option 3: Set DefaultRequestHeaders.Authorization

    client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new Authorizaiton( "OAuth", accessToken );

    Possible: This was the method used in the //build video and I still think there may be some method of converting DefaultNetworkCredentials into HttpHeaders and assigning them to the HttpClient.DefaultRequestHeadders but I do not know how and the documentation is not very thorough.


    Thursday, May 17, 2012 3:27 PM
  • After doing more searching I have found some other posts which reference the HttpClient sample:

    http://code.msdn.microsoft.com/windowsapps/HttpClient-sample-55700664

    And by looking at the source they use code similar to the following:

    httpClient = new HttpClient(handler);
    httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("Sample", "v8"));

    I was hoping there would be some function like UserAgent.AddCurrent but there is not.  Does anyone know how to assign default user credentials to the UserAgent property of the RequestHeaders?

    There is also this post:

    http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/d701a6ea-ddb0-40f6-82fb-c3bbb37234b4

    which describes another way to set HttpRequest Headers but still does not directly help for authorization.

    And finally this post:

    http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/6f2b2ce4-9b47-4c10-8941-1d9d2e56b0b9

    Which does show how to set authorization but doesn't explain how to generate the "auth" string in the post which might be what I need.

    Thursday, May 17, 2012 3:50 PM
  • SO, this is a lame ending, but it turns out it was just a matter of selecting another checkbox in the Package.appmanifest.  I didn't realize that the Private Networks ( Client & Server ) capability did not include Domain account credentials.  This whole time all I needed to do was enable the Enterprise Authentication. :(

    At least it's fixed, hope you all don't make the same mistake I have.  Be sure to check all the capabilities before you waste hours searching through the Object Browser!

    Thursday, May 17, 2012 4:55 PM
  • Hi,

    I have a problem with DefaultCredentials option and HTTPS. If I use HTTP protocol everything is ok - ntlm authorization is ok, but if I change HTTP to HTTPS on webservice side in the Thread.CurrentPrincipal I have nothing (empty strings and IsAuthenticate on false value).

    Do you have similar problem?

    My Ntlm configuration:

        internal class NtlmSelfHostConfiguration : HttpSelfHostConfiguration
        {
            public NtlmSelfHostConfiguration(string baseAddress) : base(baseAddress) { }
    
    		public NtlmSelfHostConfiguration(Uri baseAddress) : base(baseAddress) { }
    
            protected override BindingParameterCollection OnConfigureBinding(HttpBinding httpBinding)
            {
    			httpBinding.Security.Mode = HttpBindingSecurityMode.Transport;
                httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
    			return base.OnConfigureBinding(httpBinding);
            }
        }


    HttpClient creation

                    var handler = new HttpClientHandler() { UseDefaultCredentials = true };
                    httpClient = new HttpClient(handler);


    User-Password Basic authentication is working correctly, so it isn't problem with certificates

    Thanks,
    Luke

    Friday, October 19, 2012 2:20 PM