Passing Windows Authentication to Proxy using HttpClient - c#

I've trying to pass Windows Authentication to a WebProxy, so that a user doesn't have to type in his login data manually.
The use case is a proxy server which checks authentication against a LDAP/AD server, while the users have to change their password periodically.
I've got the following code:
private void button1_ClickAsync(object sender, EventArgs e) {
Url = "http://local.adress/test";
Execute();
}
private void button2_Click(object sender, EventArgs e) {
Url = "https://maps.googleapis.com/maps/api/timezone/json";
Execute();
}
private void Execute() {
var handler = new HttpClientHandler();
handler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
handler.UseDefaultCredentials = true;
handler.UseProxy = true;
handler.Proxy = WebRequest.DefaultWebProxy;
handler.Proxy.Credentials = new NetworkCredential("mydomainuser", "mydomainpassword");
//handler.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
var client = new HttpClient(handler);
Task<string> response = TestConnection(client, Url);
}
private async Task<string> TestConnection(HttpClient client, string url) {
try {
using (HttpResponseMessage result = await client.GetAsync(url)) {
string res = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
Console.WriteLine("content: " + res);
return result.ToString();
}
} catch (Exception e) {
Console.WriteLine("error: " + e.Message);
return e.ToString();
}
}
When defining the credentials manually (as you can see in the Execute method), everythings works as expected. I've checked the proxy log files to be sure the request is really forwarded through the proxy.
Since it's my goal to spare the user to type in his probably periodically changing password, I've tried to pass the credentials via the CredentialCache.DefaultNetworkCredentials (I've also tried CredentialCache.DefaultCredentials). While executing the request the proxy logs an DENIED and my client returns HTTP error code 407.
Am I missing something obvious? I know there are countless questions on this topic but nothing seems to solve this problem.

You have to define proxy and main URL in code.
var TARGETURL = "http://en.wikipedia.org/";
HttpClientHandler handler = new HttpClientHandler()
{
Proxy = new WebProxy("http://127.0.0.1:8888"),
UseProxy = true,
};
try this.
handler.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
handler.Credentials = CredentialCache.DefaultNetworkCredentials;
ok so your webserivces uses windows authentication.
Your desktop client is working under your credential you need impersonation
https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.windowsidentity.impersonate?view=netframework-4.8
check this if it works for you if it is basic authentication.
HttpClient client = new HttpClient(handler);
**var byteArray = Encoding.ASCII.GetBytes("username:password1234");**
**client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));**

Related

Is there a way to use an HTTPS proxy in .NET 6?

I need to check if certain HTTPS proxies are working. To do so, I originally thought of using the following code:
var proxy = new WebProxy($"https://proxy-server:port");
var handler = new HttpClientHandler { Proxy = proxy };
using (var client = new HttpClient(handler))
{
client.BaseAddress = new Uri("https://base-address");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
try
{
var content = client.GetStringAsync(filepath).Result;
Console.WriteLine(content);
}
catch (Exception e)
{
Console.WriteLine("Failed. Reason: " + e);
}
}
If I try to access the base-address without the proxy, it works fine. Using the proxy I get an error saying I can only use HTTP or SOCKS proxies. The problem is, I wasn't able to find an alternate solution that supports HTTPS proxies.
You're getting the error because the WebProxy class in .NET does not support HTTPS proxies. However, you can use the HttpClientHandler's SslOptions property to configure the SSL/TLS settings.
var proxy = new WebProxy($"http://proxy-server:port");
var handler = new HttpClientHandler
{
Proxy = proxy,
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true
}
};
using (var client = new HttpClient(handler))
{
client.BaseAddress = new Uri("https://base-address");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
try
{
var content = client.GetStringAsync(filepath).Result;
Console.WriteLine(content);
}
catch (Exception e)
{
Console.WriteLine("Failed. Reason: " + e);
}
}
Create a new instance of HttpClientHandler and set its Proxy property to a new WebProxy that represents the HTTP proxy server.
Set the SslOptions property to a new instance of SslClientAuthenticationOptions and configure the RemoteCertificateValidationCallback to return true, indicating that you want to accept any server certificate.
Create a new instance of HttpClient and pass the handler to its constructor.
Finally, use the client to send an HTTPS GET request to the base-address website.

Azure .NET Core Appservice locks up sending 502.3 because of async calls

I have this method running in a .NET Core 2.X app running in Azure app service. I have a remote server that we use this method to call from button presses in our Angular website. that calls a remote device.
Angular button --> .NET Core app service in Azure --> another app service --> internet\cell connected device. We wait for the response from the device to return a status code.
If I quickly send commands [2 or 3 in a second] to this method it causes the app service to stop responding until I restart it. I read this post and added the [, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false)].
However I can still freeze the entire app and require a restart from quickly sending commands to this method.
private async void SetEndPointValueAsync(string stunnelUrl, string username, string password)
{
try
{
//set the user name and password
var httpClientHandler = new HttpClientHandler()
{
Credentials = new NetworkCredential(username, password)
};
using (var client = new HttpClient(httpClientHandler))
{
using (var response = await client.GetAsync(stunnelUrl**, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false)**)
{
if (response.IsSuccessStatusCode)
{
LogInfo(typeof(IntegrationService), stunnelUrl, LogAction.EndpointUpdate);
}
else
{
//request failed.
LogWarning(typeof(IntegrationService), stunnelUrl, LogAction.DeviceRequest);
}
//using (var content = response.Content)
//{
// //do here your changes when required
//}
}
}
}
catch (Exception e)
{
LogErrorDetailed(e, typeof(IntegrationService), stunnelUrl, LogAction.DeviceRequest);
}
}
Generally, you don't want to create so many instances of HttpClient as you lose a lot of the benefits of the management it provides.
You could reduce some overhead by only having a single instance of it, like so...
private readonly HttpClient _client;
public ClassConstructor(HttpClient client)
{
_client = client ?? new HttpClient();
}
Then you could change your method to look something like this...
private async Task SetEndPointValueAsync(Uri stunnelUri, string username, string password)
{
if (stunnelUri == null) throw new ArgumentNullException(nameof(stunnelUri));
if (String.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username));
if (String.IsNullOrEmpty(password)) throw new ArgumentNullException(nameof(password));
byte[] byteArray = Encoding.ASCII.GetBytes($"{username}:{password}");
string scheme = "Basic";
string parameter = Convert.ToBase64String(byteArray);
HttpRequestMessage request = new HttpRequestMessage();
request.Method = HttpMethod.Post;
request.RequestUri = stunnelUri;
request.Headers.Authorization = new AuthenticationHeaderValue(scheme, parameter);
try
{
HttpResponseMessage response = await _client.SendAsync(request);
// This will throw an HttpRequestException if there is a failure above
response.EnsureSuccessStatusCode();
// do your stuff here...
// { ... }
}
catch (HttpRequestException)
{
// { log your connection / bad request issue. }
}
catch (Exception)
{
// I don't recommend this, but since you had it...
// { handle all other exceptions }
}
}

How do I call API from server side using ASP.NET MVC?

I am trying to pull REST data from an API but I need to handle the calls to the API with some server side solution. I have tried using the following code
try
{
HttpClient client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(60);
var request = new HttpRequestMessage()
{
RequestUri = new Uri(string.Format("https://jsonodds.com/{0}{1}{2}", "api/odds/", "?source=", "3")),
Method = HttpMethod.Get,
};
request.Headers.Add("JsonOdds-API-Key", "your key");
HttpResponseMessage response = client.SendAsync(request).Result;
if (response.IsSuccessStatusCode)
{
String.Format("Success");
}
}
catch (Exception ex)
{ //log error }
I receive a 407() error. Any ideas or tips how to do this?
If you are going through a proxy server then you need to use a different constructor for HttpClient.
_httpClient = new HttpClient(new HttpClientHandler
{
UseProxy = true,
Proxy = new WebProxy
{
Address = new Uri(proxyUrl),
BypassProxyOnLocal = false,
UseDefaultCredentials = true
}
})
{
BaseAddress = url
};
Replace proxyUrl with your proxy address then replacing the credential with those that are valid for your proxy. This example uses the default credentials, but you can pass a NetworkCredential to the WebProxy.

Set 'per request' cookie with .Net HttpClient

Hi I'm trying to implement simple http proxy service using Owin infrastructure. This proxy must authenticate user with windows authentication, pull user's properties from the enterprise AD, add this info as a cookie value into original request and then redirect request to the application on the Internet (lets call it External Application).
I'm using HttpClient to send request to the External Application.
However HttpClient does not fit well for this scenario. It seems that the only allowed way to send cookie using it is to put them into CookieContainer and set this CookieContainer as a property of HttpClientHandler. It's ok when you have single user but in case of a proxy service cookie values from different users will mix and overwrite each other.
Is there any way to set cookie or CookieContainer per request? Or maybe there is a better way to redirect requests?
P.S. Here is some code:
Initialization of http handlers:
private void RegisterRoutes(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "Proxy",
routeTemplate: "{*path}",
handler: HttpClientFactory.CreatePipeline
(
innerHandler: new HttpClientHandler(),
handlers: new DelegatingHandler[]
{
new ProxyHandler()
}
),
defaults: new { path = RouteParameter.Optional },
constraints: null);
}
ProxyHandler
internal class ProxyHandler : DelegatingHandler
{
private readonly HttpClient _client;
public ProxyHandler()
{
var handler = new HttpClientHandler { ClientCertificateOptions = ClientCertificateOption.Automatic};
_client = new HttpClient(handler);
}
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var forwardUri = new UriBuilder(request.RequestUri);
forwardUri.Host = "localhost";
forwardUri.Port = 23016;
forwardUri.Scheme = Uri.UriSchemeHttp;
request.RequestUri = forwardUri.Uri;
request.Headers.Host = forwardUri.Host;
//Explicitly null it to avoid protocol violation
if (request.Method == HttpMethod.Get || request.Method == HttpMethod.Trace)
request.Content = null;
try
{
var response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
//Explicitly null it to avoid protocol violation
if (request.Method == HttpMethod.Head)
response.Content = null;
return response;
}
catch (Exception ex)
{
var response = request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
string message = ex.Message;
if (ex.InnerException != null)
message += ':' + ex.InnerException.Message;
response.Content = new StringContent(message);
Trace.TraceError("Error:{0}", message);
return response;
}
}
private void SetCookies(HttpRequestMessage request)
{
var container = new CookieContainer();
var authCookieValue = "2EF91D8FD9EDC594F2DB82";
var authCookie = new Cookie("cookieByProxy", authCookieValue);
var targetUri = new Uri("http://localhost:23016/");
container.Add(targetUri, authCookie);
var cookieHeader = container.GetCookieHeader(targetUri);
if (!string.IsNullOrEmpty(cookieHeader))
request.Headers.TryAddWithoutValidation("Cookie", cookieHeader);//Overwriting cookie header with custom values. However cookie values are ignored by HttpClient (both old and new)
}
}
Found solution here: enter link description here
The trick is in explicitly setting UseCookie flag of the HttpClientHandler to false.
var handler = new HttpClientHandler { ClientCertificateOptions = ClientCertificateOption.Automatic, UseCookie = false };

Consuming WebApi Self Host over SSL/TLS

I have a WebApi self hosted console server over SSL.
The server takes care of creating the SSL certificates, adding them to the certificate store and binds the certificate to the port using netsh all on the fly.
The server has a simple controller that returns the string "Hello World" through HTTP GET.
I can access it through the browser without any problems and I am quite certain there is nothing wrong with the server code so I am only going to post the troubled client code here.
private static string url = #"https://localhost:4443/WebApi/Service/HelloWorld;
private static async Task GetHelloWorldRequest(string url)
{
using (HttpClient httpClient = new HttpClient(GetSSLHandler()))
{
HttpRequestMessage request = new HttpRequestMessage();
request.Method = HttpMethod.Get;
request.RequestUri = new Uri(url);
await httpClient
.SendAsync(request)
.ContinueWith((response)
=>
{
try
{
ProcessResponse(response);
}
catch (AggregateException agException)
{
throw new Exception("Error getting response: " + agException.Message);
}
}).ConfigureAwait(false);
}
}
private static void ProcessResponse(Task<HttpResponseMessage> response)
{
Console.WriteLine(response.Result.Content.ReadAsStringAsync().Result);
}
private static void ProcessResponseHeaders(Task<HttpResponseMessage> response)
{
Console.WriteLine(response.Result.Headers.ToString());
}
private static WebRequestHandler GetSSLHandler()
{
WebRequestHandler handler = new WebRequestHandler();
X509Certificate2 certificate = GetMyX509Certificate();
handler.ClientCertificates.Add(certificate);
return handler;
}
Now in my main routine I simply call this:
Console.WriteLine("Response headers:");
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
Task task = GetHelloWorldRequest(url);
task.Wait();
Now my problem is if I try to read the response content which should give me "Hello World" it gives me an empty string instead.
So I tried looking at the response headers and this is what I get:
It seems to be it is going through the negotiation phase and I don't know what to do from here.
Please advice. Thank you in advance.
EDIT:
Sorry the problem is with the server code. Simply uncomment the section calling httpBinding.ConfigureTransportBindingElement.
class SslHttpsSelfHostConfiguration : HttpSelfHostConfiguration
{
public SslHttpsSelfHostConfiguration(string baseAddress) : base(baseAddress) { }
public SslHttpsSelfHostConfiguration(Uri baseAddress) : base(baseAddress) { }
protected override BindingParameterCollection OnConfigureBinding(HttpBinding httpBinding)
{
httpBinding.Security.Mode = HttpBindingSecurityMode.Transport;
/*
httpBinding.ConfigureTransportBindingElement = (element =>
element.AuthenticationScheme =
AuthenticationSchemes.Negotiate);
*/
return base.OnConfigureBinding(httpBinding);
}
}
Please try running this:
var client = new HttpClient
{
BaseAddress = new Uri("https://localhost:4443/")
};
var result = await client.GetAsync("WebApi/Service/HelloWorld").ConfigureAwait(false);
var data = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
return data;
as your Request task.
And try changing the
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
to
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;

Categories