Xamarin.Mac - How to pass current credentials to REST endpoint - c#

Unable to use the System.Net API to authenticate current user to a REST endpoint. Example below returns 401 unauthorized
using (HttpClient client = new HttpClient())
{
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "http://rest/api/endpoint")
{
using (httpResponseMessage response = await client.SendAsync(request))
{
if (response.IsSuccessStatusCode)
//do something
}
}
}
If I use NSUrlConnection I am able to authenticate not problem. So, NSUrlConnection must be passing the credentials some how. Below is a snippet of that code:
var request = new NSMutableUrlRequest(new NSUrl("http://rest/api/endpoint"), NSUrlRequestCachePolicy.ReloadIgnoringCacheData, 0);
request["Accept"] = "application/json";
NSUrlConnection.SendAsynchronousRequest(request, NSOperationQueue.MainQueue, delegate(NSUrlResponse, response, NSData data, NSError error)
{
// successfully authenticated and do something with response
});
I would like to wrap my service code in a PCL to share with other platforms. Therefore, I would like to get this all working within the System.Net api. Is this possible?
UPDATE:
I've tried using an HttpClientHandler and using default credentials as well as CredentialCache.DefaultNetworkCredentials. The only way to get this to work is to hardcode the credentials, which I do not want. It appears the System.Net stack does not surface the credentials from the OS.

I assume it's using default authentication. You could do that with HttpClient with an HttpClientHandler, for example:
using (var handler = new HttpClientHandler {
UseDefaultCredentials = true
})
using (var client = new HttpClient(handler))
{
var result = await client.SendAsync(...);
}

Related

How to force HttpClient to follow HTTPS -> HTTP redirect?

I need to perform an HTTP GET operation while following redirects.
using (var client = new HttpClient())
{
var response = await client.GetAsync("https://example.com");
}
Problem is that if the server returns a 302 HTTP code redirecting to http://... (not https), .NET does not follow it (for security reasons).
How do I force HttpClient to follow redirects from HTTPS to HTTP?
You may have to do a little bit of extra work:
using var client = new HttpClient();
var response = await client.GetAsync("https://example.com");
if (new[] {HttpStatusCode.Moved, HttpStatusCode.MovedPermanently}.Contains(response.StatusCode))
{
response = await client.GetAsync(response.Headers.Location);
}
// more code

Passing Credentials works for WebRequest but not for HttpClient

I'm trying to pass credentials back to a web service using HttpClient.
However, I keep getting an Unauthorized request.
However, when I try using a WebRequest it authenticates?
HttpClient:
var handler = new NativeMessageHandler
{
UseDefaultCredentials = true,
Credentials = credential
};
var client = new HttpClient(handler);
var content = _httpClientHelper.Serialize(data);
var response = await _client.PostAsync($"{_baseurl}/api/foos/List", content);
WebRequest:
HttpResponseMessage response = null;
try
{
var data = JsonConvert.SerializeObject(new
{
ViewTitle = "New",
PageCount = 60
});
var content = _httpClientHelper.Serialize(data);
using (var client = new WebClient { UseDefaultCredentials = true, Credentials = credentials })
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
client.UploadData("$"{baseurl}/api/foos/List", "POST", Encoding.UTF8.GetBytes(content));
}
I cannot figure out why one works and the other does not.
Any help or insight on this would be greatly appreciated
As noted here and here this behavior of HttpClient could be because of how HttpClientHandler is implemented.
"[..] the StartRequest method is executed in new thread with the credentials of the asp.net process (not the credentials of the impersonated user) [..]"
You might be seeing the difference in behavior of HttpClient and WebClient because
"HttpClient creates new threads
via the Task Factory. WebClient on the other hand, runs synchronously
on the same thread thereby forwarding its
credentials (i.e. the credentials of the impersonated user) ."

Passing current authentication credentials from UWP app to web service to access resources on server with these credentials

My Windows 10 UWP app is calling a WebAPI web service that I have created. I need to pass the current credentials on the client side when calling the web service so that it can access other resources using these credentials.
I also need to do this without prompting the user for credentials so that the experience is seamless.
I am able to do this with using System.Net.Http and successfully pass the current credentials to the server to use for accessing resources. This sends the request and brings back the response without any prompt. I have enabled Enterprise Authentication and Private Networks capabilities on the UWP app to make this work.
Problem: This works fine for GET requests but not for POST requests to the same server. POST requests result in the following error:
This IRandomAccessStream does not support the GetInputStreamAt method
because it requires cloning and this stream does not support cloning.
I read that this was a bug on this link: PostAsync throwing IRandomAccessStream error when targeting windows 10 UWP. The workaround proposed in multiple locations for this bug is to use Windows.Web.Http instead. However, if I do this, how can I pass the default/current credentials to the server?
Here is the code that I am using to do a GET request using the current Windows credentials without prompting for it. It works flawlessly:
System.Net.Http.HttpClientHandler handler = new System.Net.Http.HttpClientHandler
{
UseDefaultCredentials = true
// Credentials = (NetworkCredential)System.Net.CredentialCache.DefaultNetworkCredentials
//using either one of the above enables me to have the web service use the current credentials without prompting
};
string responseContent = string.Empty;
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient(handler))
{
System.Net.Http.HttpRequestMessage requestMessage = new System.Net.Http.HttpRequestMessage();
requestMessage = new System.Net.Http.HttpRequestMessage
{
Method = System.Net.Http.HttpMethod.Get,
RequestUri = new Uri(strWebServiceURL)
};
using (System.Net.Http.HttpResponseMessage response = await client.SendAsync(requestMessage))
{
responseContent = await response.Content.ReadAsStringAsync();
}
//This also works fine
using (System.Net.Http.HttpResponseMessage response = await client.GetAsync(strWebServiceURL))
{
responseContent = await response.Content.ReadAsStringAsync();
}
Below is the code I use to do a POST request which results in the IRandomAccessStream error:
System.Net.Http.HttpClientHandler handler = new System.Net.Http.HttpClientHandler
{
UseDefaultCredentials = true
// Credentials = (NetworkCredential)System.Net.CredentialCache.DefaultNetworkCredentials
//using either one of the above enables me to have the web service use the current credentials without prompting
};
string responseContent = string.Empty;
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient(handler))
{
System.Net.Http.HttpRequestMessage requestMessage = new System.Net.Http.HttpRequestMessage();
requestMessage = new System.Net.Http.HttpRequestMessage
{
Content = myMultipartFormDataContent,
Method = System.Net.Http.HttpMethod.Post,
RequestUri = new Uri(strWebServiceURL)
};
using (System.Net.Http.HttpResponseMessage response = await client.SendAsync(requestMessage))
{
responseContent = await response.Content.ReadAsStringAsync();
}
//No difference when using it this way as well
using (System.Net.Http.HttpResponseMessage response = await client.PostAsync(strWebServiceURL, myMultipartFormDataContent))
{
responseContent = await response.Content.ReadAsStringAsync();
}
I tried using Windows.Web.Http but I don't know how I can get it to pass the current/default credentials to the server without prompting.
I have also added the WebService URL to a IE Local Intranet zone and have that zone set to automatically log in with current user name and password:
Please help!
With the new Windows.Web.Http namespace in UWP app, if you want to use the DefaultCredentials, all you have to do is turn on enterprise credentials in the manifest and the uwp app will send them out as appropriate. You don't need to configure anything on the HttpClientto make it work. Details please reference this thread.
Since you already enable the enterprise credentials capability, you could just create HttpClient without configure. But to avoid the username and password prompt, you may need to disable the UI, for example:
var myFilter = new HttpBaseProtocolFilter();
myFilter.AllowUI = false;
Windows.Web.Http.HttpClient client = new Windows.Web.Http.HttpClient(myFilter);
Windows.Web.Http.HttpResponseMessage result = await client.GetAsync(new Uri("http://localhost:5132/api/values"));

.NET Core Web API - Proxy HTTP Request

I have an API which has to do the following:
Incoming HttpRequest -> Logic to decide which endpoint -> Send call to specific endpoint (another API)
HttpResult -> Logic to manipulate response -> Send response back
I was wondering whether there is a clean way to do so? Currently using something like this:
var httpRequestMessage = new HttpRequestMessage()
{
Method = new HttpMethod(Request.Method),
RequestUri = new Uri(endpoint)
};
using (HttpClient httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Add("Authorization", Request.Headers["Authorization"][0]);
using (var response = await httpClient.SendAsync(httpRequestMessage))
{
using (HttpContent content = response.Content)
{
string data = await content.ReadAsStringAsync();
}
}
}
But I'm not really happy about the conversion from HttpRequest to HttpRequestMessage. I'd just like to pass the HttpRequest with another Uri but still be able to put logic in between the requesting and receiving.
When working with proxy middleware are you still able to put logic in between the request and result?

HttpClient access url with '#' (at symbol)

I'm integrating a service that returns a key when I a GET request to a URL that is in the following format:
https://username:password#service.com/refresh.key
When I access the URL in my browser, it returns the new key as expected, by when I do a GET request using HttpClient I get a 401.
HttpClient _client = new HttpClient();
var response = await _client.GetAsync(#"https://username:password#service.com/refresh.key"); // Returns a 401
I think it has something to do with the '#' in the URL, but I'm not sure how to fix it, I tried replacing it with '%40', but when I do that I get a UriFormatException.
Does anyone know how to do this?
You should modify Authorization header of HttpClient, can you try the code below;
HttpClient _client = new HttpClient();
byte[] usernamePasswordBytes = Encoding.ASCII.GetBytes("user:pass");
_client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(usernamePasswordBytes));
var response = await _client.GetAsync(#"https://service.com/refresh.key");
PS: Such username:pass#domain.com requests are BasicAuthentication request so in fact you try to make basic authentication request.
Hope this works for you
You don't need to provide credentials in url. Instead you can do:
using (var handler = new HttpClientHandler {Credentials = new NetworkCredential("username", "password")}) {
using (HttpClient _client = new HttpClient(handler)) {
var response = await _client.GetAsync(#"https://service.com/refresh.key");
}
}

Categories

Resources