Reputation: 453
This is similar to this question; however, slightly different and the answers did not work for me (or the original asker it seems)
I have an asp.net web api project created in .NET Framework 4.5. I wanted to add basic authentication in which I verify the user credentials upon every request. I used code I found from a couple sites (I would post the links but I need more reputation to post more than 2 links) to create a BasicAuthenticationAttribute and come up with a working solution.
All this was working fine on localhost, but when I moved it to our GoDaddy shared hosting site it always returns unauthorized. This unauthorized response comes before my authorization and I have proven that by removing my authorization code, which still results in the unauthorized response. Now, the interesting thing is, if I don't add the user credentials in the request, it works fine. It's only when I add the credentials that I receive the unauthorized response.
To summarize that a little...
I have seen several posts outlining that forms authentication could be getting in the way and I need to enable anonymous authentication. All of which I've tried, but nothing has solved the issue. I have also tried removing the FormsAuthentication in the web.config by taking out the code that adds it in the first place, as well as telling it to remove forms authentication as noted here.
Relevant parts of web.config (I have commented out authentication section, I've had it uncommented and commented with same results):
<!--<authentication mode="Forms">
</authentication>-->
<membership defaultProvider="AspNetSqlMembershipProvider" userIsOnlineTimeWindow="30">
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="MyConnectionString" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" applicationName="MyApplicationName" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" passwordAttemptWindow="10" passwordStrengthRegularExpression="" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0"/>
</providers>
</membership>
<roleManager enabled="true" defaultProvider="CustomizedRoleProvider" cookieTimeout="30">
<providers>
<add name="CustomizedRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="MyConnectionString" applicationName="MyApplicationName"/>
</providers>
</roleManager>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<remove name="FormsAuthentication" />
</modules>
</system.webServer>
Client code calling the service:
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes("MyUserName" + ":" + "MyPassword"));
client.DefaultRequestHeaders.Authorization = System.Net.Http.Headers.AuthenticationHeaderValue.Parse("Basic " + credentials);
var obj = new MyObject()
{
MyData...
};
HttpResponseMessage response = await client.PostAsXmlAsync("<URI>", obj);
...
}
Unauthorized request:
POST <URI> HTTP/1.1
Accept: application/json
Authorization: Basic <encoded info>
Content-Type: application/xml; charset=utf-8
Host: <host>
Content-Length: 705
Expect: 100-continue
Connection: Keep-Alive
<data>
Unauthorized response:
HTTP/1.1 401 Unauthorized
Content-Type: text/html
Server: Microsoft-IIS/7.0
WWW-Authenticate: Basic realm="<host>"
X-Powered-By: ASP.NET
Date: Tue, 11 Feb 2014 00:52:10 GMT
Content-Length: 1293
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>401 - Unauthorized: Access is denied due to invalid credentials.</title>
<style type="text/css">
<!--
body{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}
fieldset{padding:0 15px 10px 15px;}
h1{font-size:2.4em;margin:0;color:#FFF;}
h2{font-size:1.7em;margin:0;color:#CC0000;}
h3{font-size:1.2em;margin:10px 0 0 0;color:#000000;}
#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS", Verdana, sans-serif;color:#FFF;
background-color:#555555;}
#content{margin:0 0 0 2%;position:relative;}
.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}
-->
</style>
</head>
<body>
<div id="header"><h1>Server Error</h1></div>
<div id="content">
<div class="content-container"><fieldset>
<h2>401 - Unauthorized: Access is denied due to invalid credentials.</h2>
<h3>You do not have permission to view this directory or page using the credentials that you supplied.</h3>
</fieldset></div>
</div>
</body>
</html>
Upvotes: 3
Views: 3121
Reputation: 453
I had never figured out the issue and needed a solution so I had to alter the object the client sends to include the user name and a guid to identify them and have nothing for the authorization header. That's an interesting workaround. The info you wrote about basic authorization in iis helps in case we move to dedicated and experience the same issue, thanks!
Upvotes: 0
Reputation: 282
After searching and testing I finally figured out the problem.
In GoDaddy's shared hosting environment, Basic Authentication is automatically and permanently enabled on all IIS Applications. You can optionally check a box in GoDaddy's IIS Management web portal to enable Anonymous. If you do that, it is the equivalent of having both Basic and Anonymous enabled in IIS under the "Authentication" icon.
This is easy enough to recreate in local IIS. The problem for me was that on my local machine, I did not have the Basic Authentication feature installed, so it wasn't even an option. Once I installed it and enabled it, I got the exact symptom I was getting from GoDaddy.
Since IIS in this scenario is configured to look for Basic Authentication, it sees the Authorization header on your request and attempts to do the work instead of passing it to your code. This makes sense, but isn't the desired behavior.
The desired behavior can only be achieved by disabling Basic Authentication on the IIS Application. This is where GoDaddy's shared hosting becomes a pain - they won't or can't disable Basic Authentication. The answer I got was that I need a dedicated hosting environment if I want this level of control.
WORKAROUND (edit)
If you are a stickler for following standards, stop reading :-). If you change the scheme portion of the Authorization header to something other than Basic, IIS will ignore the header and pass it on through! That means you can change to something like this:
Authorization: Pickle cmFtc2V5amFjb2I6aXNfYV9yYWRfZHVkZQ==
Then just change your code to look for Pickle and it should work (it worked for me in my tests). Eat that, GoDaddy!
Upvotes: 6