Authorization header is lost on redirect
The reason you are experiencing this behavior is that it is by design.
Most HTTP clients (by default) strip out authorization headers when following a redirect.
One reason is security. The client could be redirected to an untrusted third party server, one that you would not want to disclose your authorization token to.
What you can do is detect that the redirect has occurred and reissue the request directly to the correct location.
Your API is returning 401 Unauthorized
to indicate that the authorization header is missing (or incomplete). I will assume that the same API returns 403 Forbidden
if the authorization information is present in the request but is simply incorrect (wrong username / password).
If this is the case, you can detect the 'redirect / missing authorization header' combination and resend the request.
Here is the code from the question rewritten to do this:
[Test]
public void RedirectTest()
{
// These lines are not relevant to the problem, but are included for completeness.
HttpResponseMessage response;
var client = new HttpClient();
using (var authString = new StringContent(@"{username: ""theUser"", password: ""password""}", Encoding.UTF8, "application/json"))
{
response = client.PostAsync("http://host/api/authenticate", authString).Result;
}
string result = response.Content.ReadAsStringAsync().Result;
var authorization = JsonConvert.DeserializeObject<CustomAutorization>(result);
// Relevant from this point on.
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authorization.Scheme, authorization.Token);
client.DefaultRequestHeaders.Add("Accept", "application/vnd.host+json;version=1");
var requestUri = new Uri("http://host/api/getSomething");
response = client.GetAsync(requestUri).Result;
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
// Authorization header has been set, but the server reports that it is missing.
// It was probably stripped out due to a redirect.
var finalRequestUri = response.RequestMessage.RequestUri; // contains the final location after following the redirect.
if (finalRequestUri != requestUri) // detect that a redirect actually did occur.
{
if (IsHostTrusted(finalRequestUri)) // check that we can trust the host we were redirected to.
{
response = client.GetAsync(finalRequestUri).Result; // Reissue the request. The DefaultRequestHeaders configured on the client will be used, so we don't have to set them again.
}
}
}
Assert.True(response.StatusCode == HttpStatusCode.OK);
}
private bool IsHostTrusted(Uri uri)
{
// Do whatever checks you need to do here
// to make sure that the host
// is trusted and you are happy to send it
// your authorization token.
if (uri.Host == "host")
{
return true;
}
return false;
}
Note that you could save the value of finalRequestUri
and use it for future requests to avoid the extra request involved in the retry. However as this is a temporary redirect you should probably issue the request to the original location each time.
Nginx redirection to Phoenix loses authorization header
This is a feature in curl. If a request gets redirected to a different hostname, then any Authorization
header is going to be removed in the second request in order not to leak credentials to an unrelated server. (You're making a request to localhost:80
, but the redirect location is 0.0.0.0:4000
, so that counts as a different hostname.)
You can get curl to forward the Authorization
header by using the --location-trusted
option instead of -L
.
(Though it's odd that you're seeing this with curl 7.54.0 - according to this security advisory, curl 7.54.0 should behave as you expect it to, and only 7.58.0 and higher have this protection feature.)
NodeJS: header authorization missing from redirect using res.writeHead
Headers like auth headers and other custom headers are NOT preserved by the browser when you do a redirect. The browser forms a new request to the location
you specified in the redirect response and builds that new request with default headers, not with the headers from your redirect response. It is as if the user typed the redirected URL into the browser URL bar and the browser created a new, default request to that new URL.
You will need to pass information to the redirected request either via the URL path, via query parameters or via a cookie. Query parameters and the URL path will always be present on the redirected request and cookies will be present if the cookie is set on the domain of the redirected target.
When the "new" request arrives to the other NodeJS server, the authorization header is missing
Headers are not preserved when redirecting the browser. The browser creates a new request with default headers to the location
specified in the redirect. Any information you want to communicate to the new target must be either in the redirect URL (path or query parameters) or in a cookie that is set on the target domain.
How to prevent Safari from dropping the Authorization header when following a same-origin redirect?
Safari 15.4+ (iOS 15.4, macOS 12.3) users will no longer experience this Authorization
drop.
Details: A fix for this was actually merged into the WebKit/Safari code back in October 2021 but per comments at https://bugs.webkit.org/show_bug.cgi?id=230935#c18 didn’t end up shipping until mid-March 2022 in Safari 15.4.
So, this problem will continue to happen for any users of Safari versions prior to to 15.4 — users not upgraded to iOS 15.4 or macOS 12.3 yet — but not for users with current Safari/iOS/macOS.
NSURLRequest lost HTTP header “Authorization” while redirecting the request
'Authorization' header is one from the 'special' headers that are advised not to be modified. This is from Apple's documentation (LINK):
The NSURLConnection class and NSURLSession classes are designed to handle various aspects >of the HTTP protocol for you. As a result, you should not modify the following headers:
- Authorization
- Connection
- Host
- WWW-Authenticate
Passing Authorization Header in multiple redirects of HttpClient
I was missing the delegation handler, which will handle the redirects.
https://stackoverflow.com/a/19493338/3459965
this link helps me
Updated Code is:
static async Task CallWebAPIAsync()
{
HttpClientHandler clientHandler = new HttpClientHandler();
WebRequestHandler webRequestHandler = new WebRequestHandler();
webRequestHandler.UseDefaultCredentials = true;
webRequestHandler.AllowPipelining = true;
webRequestHandler.AllowAutoRedirect = false;
webRequestHandler.Credentials = CredentialCache.DefaultCredentials;
GlobalRedirectHandler globalRedirectHandler = new GlobalRedirectHandler() { InnerHandler = webRequestHandler };
using (var client = new HttpClient(globalRedirectHandler))
{
client.BaseAddress = new Uri("https://apitest");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token);
client.Timeout = TimeSpan.FromSeconds(30000);
//GET Method
var response = await client.GetAsync("user").ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var responseresult = await response.Content.ReadAsStringAsync();
Console.WriteLine("Id:{0}\tName:{1}", responseresult);
}
else
{
Console.WriteLine("Internal server Error");
}
}
}
C# HttpClient authorization header removed after send to server
I find out the problem. It's really Ridiculous.
When I use URL like www.abc.com/api/something
the request gets 301 error and postman sends another request like www.abc.com/api/something/
. The difference is just /
at the end of request.
I tried new URL in postman and first request got ok.
Also I tried URL in my C# code and again its ok.
But i could not understand why.
Thanks a lot dear @pharaz-fadaei
Related Topics
What Advantages of Extension Methods Have You Found
Resizing an Image in ASP.NET Without Losing the Image Quality
Linq to SQL and a Running Total on Ordered Results
How to Unload an Assembly from the Primary Appdomain
How to Set the Default Xml Namespace for an Xdocument
How to Select Text from the Richtextbox and Then Color It
Why Can't C# Interfaces Contain Fields
How to Convert List<String> to List<Int>
How to "Kill" Background Worker Completely
Anonymous Type Result from SQL Query Execution Entity Framework
How to Add Claims in ASP.NET Identity
"Could Not Load Type [Namespace].Global" Causing Me Grief
How to Implement a Custom Razorviewengine to Find Views in Non-Standard Locations
How to Modify or Delete Items from an Enumerable Collection While Iterating Through It in C#
Zoom and Translate an Image from the Mouse Location
What Does the Word "Literal" Mean
What's the Difference Between "Groups" and "Captures" in .Net Regular Expressions