Reputation: 12887
My code is making an HTTP GET to a web service URL that requires basic authentication.
I've implemented this using an HttpClient
with an HttpClientHandler
that has the Credentials
property defined.
This all works perfectly.. Except for one of my use-cases where I'm making the authenticated GET to:
http://somedomain.com
which redirects to http://www.somedomain.com
.
It seems that the HttpClientHandler clears the authentication header during the redirect. How can I prevent this? I want the credentials to be sent regardless of redirects.
This is my code:
// prepare the request
var request = new HttpRequestMessage(method, url);
using (var handler = new HttpClientHandler { Credentials = new NetworkCredential(username, password) , PreAuthenticate = true })
using (var client = new HttpClient(handler))
{
// send the request
var response = await client.SendAsync(request);
Note: this is a related question: Keeping HTTP Basic Authentification alive while being redirected But since I'm using different classes for making the request, there might be a better, more specific solution
Upvotes: 6
Views: 7127
Reputation: 31733
I used @DarrelMiller 's solution and it works. However, I did some improvements
I refactored the code so everything is in CopyRequest
which now takes the response
as an argument.
var newRequest = CopyRequest(response);
base.SendAsync(newRequest, cancellationToken)
.ContinueWith(t2 => tcs.SetResult(t2.Result));
This is the CopyRequest method with my improvements
StreamContent
and set it to null for Redirect / Found / SeeOther
the content is only set if necesarry.private static HttpRequestMessage CopyRequest(HttpResponseMessage response)
{
var oldRequest = response.RequestMessage;
var newRequest = new HttpRequestMessage(oldRequest.Method, oldRequest.RequestUri);
if (response.Headers.Location != null)
{
if (response.Headers.Location.IsAbsoluteUri)
{
newRequest.RequestUri = response.Headers.Location;
}
else
{
newRequest.RequestUri = new Uri(newRequest.RequestUri, response.Headers.Location);
}
}
foreach (var header in oldRequest.Headers)
{
if (header.Key.Equals("Authorization", StringComparison.OrdinalIgnoreCase) && !(oldRequest.RequestUri.Host.Equals(newRequest.RequestUri.Host)))
{
//do not leak Authorization Header to other hosts
continue;
}
newRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
foreach (var property in oldRequest.Properties)
{
newRequest.Properties.Add(property);
}
if (response.StatusCode == HttpStatusCode.Redirect
|| response.StatusCode == HttpStatusCode.Found
|| response.StatusCode == HttpStatusCode.SeeOther)
{
newRequest.Content = null;
newRequest.Method = HttpMethod.Get;
}
else if (oldRequest.Content != null)
{
newRequest.Content = new StreamContent(oldRequest.Content.ReadAsStreamAsync().Result);
}
return newRequest;
}
Upvotes: 0
Reputation: 142094
The default HttpClientHandler uses the same HttpWebRequest infrastructure under the covers. Instead of assigning a NetworkCredential to the Credentials property, create a CredentialCache and assign that.
This is what I use in place of the AutoRedirect and with a little async/await fairy dust it would probably be a whole lot prettier and more reliable.
public class GlobalRedirectHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
var tcs = new TaskCompletionSource<HttpResponseMessage>();
base.SendAsync(request, cancellationToken)
.ContinueWith(t => {
HttpResponseMessage response;
try {
response = t.Result;
}
catch (Exception e) {
response = new HttpResponseMessage(HttpStatusCode.ServiceUnavailable);
response.ReasonPhrase = e.Message;
}
if (response.StatusCode == HttpStatusCode.MovedPermanently
|| response.StatusCode == HttpStatusCode.Moved
|| response.StatusCode == HttpStatusCode.Redirect
|| response.StatusCode == HttpStatusCode.Found
|| response.StatusCode == HttpStatusCode.SeeOther
|| response.StatusCode == HttpStatusCode.RedirectKeepVerb
|| response.StatusCode == HttpStatusCode.TemporaryRedirect
|| (int)response.StatusCode == 308)
{
var newRequest = CopyRequest(response.RequestMessage);
if (response.StatusCode == HttpStatusCode.Redirect
|| response.StatusCode == HttpStatusCode.Found
|| response.StatusCode == HttpStatusCode.SeeOther)
{
newRequest.Content = null;
newRequest.Method = HttpMethod.Get;
}
newRequest.RequestUri = response.Headers.Location;
base.SendAsync(newRequest, cancellationToken)
.ContinueWith(t2 => tcs.SetResult(t2.Result));
}
else {
tcs.SetResult(response);
}
});
return tcs.Task;
}
private static HttpRequestMessage CopyRequest(HttpRequestMessage oldRequest) {
var newrequest = new HttpRequestMessage(oldRequest.Method, oldRequest.RequestUri);
foreach (var header in oldRequest.Headers) {
newrequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
foreach (var property in oldRequest.Properties) {
newrequest.Properties.Add(property);
}
if (oldRequest.Content != null) newrequest.Content = new StreamContent(oldRequest.Content.ReadAsStreamAsync().Result);
return newrequest;
}
}
Upvotes: 4