1

I am trying to make a GET api request with the below C# code but it fails with

System.Net.WebException: The remote server returned an error: (401) Unauthorized.
   at System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request)
   at System.Net.WebClient.DownloadString(Uri address)
   at System.Net.WebClient.DownloadString(String address)
   at Rextester.Program.callAPI()
   at Rextester.Program.Main(String[] args)

Note that the same API request works through postman.

private static void callAPI()
{

    WebClient client = new WebClient();
    client.UseDefaultCredentials = false;            

    System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;

    client.Headers.Add("Authorization", "Basic " + Base64Encode("<id:password>"));            
    client.Headers.Add("Content-Type", "application/json");   
    client.Headers.Add("Environment-Id", "325");

    client.QueryString.Add("query", "%7B%7D");

    string reply = client.DownloadString("https://<server-api-url>");

    Console.WriteLine(reply);
}        


private static string Base64Encode(string plainText)
{
    var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
    var encodedS = System.Convert.ToBase64String(plainTextBytes);
    Console.WriteLine(encodedS);
    return encodedS;
}

One more strange fact is that I see the exception printed before the encoded string. It should I think the other way round. The encoded string should be printed before since the method call happens before making the api call.

Ashish Kamble
  • 2,555
  • 3
  • 21
  • 29
Andy Dufresne
  • 6,022
  • 7
  • 63
  • 113
  • Where's the API request that works with Postman? If you use a debugging proxy like Fiddler to see what's actually going on I'll bet you'll find the requests are *not* the same. – Panagiotis Kanavos Nov 19 '18 at 08:14
  • What does `""` represent? In Basic authentication the string should be `username:password`, eg `$"{username}:{password}"` or `username + ":" + password` or `String.Format("{0}:{1}", username,password)` – Panagiotis Kanavos Nov 19 '18 at 08:20
  • I have added dummy values for not exposing production environment details. You are right about the format of the authentication string. It is in the format "username:password". – Andy Dufresne Nov 19 '18 at 08:25
  • You are forcing people to guess right now. Are you using `<>` or not? If yes, that's a mistake. If not, there's nothing else that can help understand what's wrong. What *should* the request look like? What did the Postman request look like in Fiddler? What did the WebClient look like? If the authentication headers are different you'll know the `Base64Encode` methods is wrong.If there's another difference, what is it? – Panagiotis Kanavos Nov 19 '18 at 08:30
  • Thanks for pointing me in the right direction. Through fiddler I understood that the api call is actually getting redirected (with http status code 307) and on the second request the authorization header is not being passed. How do I enable redirects in C#? – Andy Dufresne Nov 19 '18 at 09:06
  • Just don't use WebClient, it's obsolete since 2010 and C# 4.0. It's been replaced by [HttpClient](https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.118%29.aspx). In WebClient you'd have to intercept the HttpWebRequest call to change the AllowAutoRedirect property. In HttpClient autoredirect is on by default. You have to [configure it explicitly](https://stackoverflow.com/questions/10642528/how-does-one-configure-httpclient-not-to-automatically-redirect-when-it-receives) to disable autoredirects. – Panagiotis Kanavos Nov 19 '18 at 09:18
  • Another huge benefit is that HttpClient is reusable and thread-safe. You can use the same HttpClient for multiple concurrent calls, saving time on DNS resolution *and* the TLS handshake. You can use reliability libraries like Polly with HttpClient [as shown in this article](https://learn.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/implement-http-call-retries-exponential-backoff-polly) – Panagiotis Kanavos Nov 19 '18 at 09:21
  • I was able to make the sample work with RestSharp library. The redirect problem still remains though. The authorization header is getting removed with the redirect. How do I prevent it with RestSharp? – Andy Dufresne Nov 19 '18 at 10:28
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/183885/discussion-between-andy-dufresne-and-panagiotis-kanavos). – Andy Dufresne Nov 19 '18 at 12:03

1 Answers1

2

After getting some guidance from @Panagiotis Kanavos I traced the api calls from Fiddler and understood that the api call was getting redirected (http status code - 307). The C# client is by default configured to not pass the authorization header on a redirect. Hence I got a 401 Unauthorized error. This was fixed by adding a check for 401 status code and resending the api request with the authorization header.

Thanks @Panagiotis Kanavos for the help

Andy Dufresne
  • 6,022
  • 7
  • 63
  • 113