Question

I'm tearing my hair out on this one, I have a WCF service that I can call through the browser and it works fine, when I call it from the web application with the below method I get a (401) Unauthorized error. And the service does not get called. What's more, when I run my web application from my local machine (debug mode using IIS Express) pointed at my dev server (IIS7) it works but when I deploy my web application to the dev server and point it to the dev server services it fails wit the 401 error. I think this is something to do with IIS7 but I'm not 100% sure and help would be super useful.

I have looked online for the answers but thus far the best I have found is this.

My service call is as follows:

var request = (HttpWebRequest) WebRequest.Create(url);
request.Method = "GET";
request.ContentType = "application/json; charset=utf-8";
request.AuthenticationLevel = AuthenticationLevel.MutualAuthRequested;
request.Credentials = CredentialCache.DefaultCredentials;

WebResponse responce = request.GetResponse();
Stream reader = responce.GetResponseStream();

var sReader = new StreamReader(reader);
string outResult = sReader.ReadToEnd();
sReader.Close();

var result = (T) JsonConvert.DeserializeObject(outResult, typeof (T));
return result;

My configuration for the service looks like this :

  <service name="RGMPServices.Householding.Services.AccountService" behaviorConfiguration="Default">
    <endpoint address="" kind="webHttpEndpoint" endpointConfiguration="SecuredHttpEndpointBinding" contract="RGMPServices.Householding.Contracts.IAccountService" />
  </service>

  <service name="RGMPServices.Householding.Services.HouseholdService" behaviorConfiguration="Default">
    <endpoint address="" kind="webHttpEndpoint" endpointConfiguration="SecuredHttpEndpointBinding" contract="RGMPServices.Householding.Contracts.IHouseholdService" />
  </service>

  <service name="RGMPServices.Householding.Services.UserService" behaviorConfiguration="Default">
    <endpoint address="" kind="webHttpEndpoint" endpointConfiguration="SecuredHttpEndpointBinding" contract="RGMPServices.Householding.Contracts.IUserService" />
  </service>
</services>

<behaviors>
  <endpointBehaviors>
    <behavior name="webBehaviour">
      <webHttp />
    </behavior>
  </endpointBehaviors>
  <serviceBehaviors>
    <behavior name="Default">
      <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>

<standardEndpoints>
  <webHttpEndpoint>
    <standardEndpoint name="SecuredHttpEndpointBinding" helpEnabled="true" automaticFormatSelectionEnabled="true">
      <security mode="TransportCredentialOnly">
        <transport clientCredentialType="Windows" />
      </security>
    </standardEndpoint>
  </webHttpEndpoint>
</standardEndpoints>

I have put some logging on the client service call, just before I call the service, the response is:

DEBUG 2013-10-01 13:15:13,569 452ms ServiceGetSingle - Passing Login: MYLANDOMAIN\MYLANUSERNAME

ERROR 2013-10-01 13:15:13,631 514ms ServiceGetSingle - ERROR Calling ServiceGetSingle with user credentials login: MYLANDOMAIN\MYLANUSERNAME System.Net.WebException: The remote server returned an error: (401) Unauthorized. at System.Net.HttpWebRequest.GetResponse() at Householding.Common.ServiceHelper.ServiceGetSingle[T](String url)

The code looks like:

logger.Debug("Passing Login: "
    + System.Security.Principal.WindowsIdentity.GetCurrent().Name)

Even when I set the AppPool for my website to my domain account it is still not authorising me to access the WCF Service, but again: it's working for the browser. So weird!

Was it helpful?

Solution

It seems likely you're a victim of the double-hop issue when using Integrated Windows Authentication (IWA) and Kerberos. The first hop is from your browser to the web application; the second hop is from your web application to the WCF service.

Here are some resources that explain the issue more fully, and may offer a solution:

You can configure Active Directory to support Kerberos delegation (usually the infrastructure guys don't like this), or you could turn off impersonation and use a "service" account for the web application and IIS app pool that can authenticate with the WCF service on behalf of the end user.

OTHER TIPS

What are the default credentials on the Dev server? Try doing a log right there and see what you get.

This is what I suspect: Running locally, the credentials are YOUR windows creds. When you call the dev server from dev, the credentials would be whatever account the website is running under. If that particular account doesn't have access, then it would blow up.

How they said before, this looks like an impersonation problem. Have you tried to start the client program with "run as" to change the credentials?

Additionally you can change this line of code

request.Credentials = CredentialCache.DefaultCredentials;

to

request.Credentials = new NetworkCredential("MyUsername", "MyPassword");

And see if it works. Also you need to create the account "MyUserName" with "MyPassword" on the web server to make it work.

These errors can be caused when the authenticated user does not have access to the physical path where the WCF service is hosted. On the dev server, open up IIS Manager and navigate to the virtual directory for the service. On the right in the Actions bar, click on "Basic Settings". Below the "Physical Path" text box, click "Connect as...". Choose "Specific User" and try setting it to a user account that you know has rights to the physical folder on the dev server. Typically, this would be a service account whose password does not expire.

When running from the browser, the browser is sending your auth credentials. Also, iis express will run as the logged in user, so this is also sending your credentials. Iis is different, it will be running as a local account. Even if you have authentication on your front end iis, that will not be passed to the backend. Windows impersonation tokens are limited in the number of hops allowed, usually 0. This is done to prevent exactly what you are doing. If you want front end authentication to flow to the backend then you should probably do the authentication yourself and grab user/pass on the way through. Alternatively, if you do the authentication yourself you can create an impersonation token that allows a hop to another machine and it should work.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top