9
votes

I have a WCF web service, and a client both on the same machine. Accessing the WCF web service directly using the browser works, but the client can't connect; error message below. Any ideas? Integrated Windows Auth in IIS is used for both client and server.

The remote server returned an error: (401) Unauthorized. 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.Net.WebException: The remote server returned an error: (401) Unauthorized.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.  

Stack Trace: 

[WebException: The remote server returned an error: (401) Unauthorized.]
   System.Net.HttpWebRequest.GetResponse() +5313085
   System.ServiceModel.Channels.HttpChannelRequest.WaitForReply(TimeSpan timeout) +54

[MessageSecurityException: The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'.]
   System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) +7594687
   System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) +275
   HRPaysService.IService1.GetAlert() +0
   HRPaysService.Service1Client.GetAlert() +15
   _Default.Page_Load(Object sender, EventArgs e) +138
   System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +14
   System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +35
   System.Web.UI.Control.OnLoad(EventArgs e) +99
   System.Web.UI.Control.LoadRecursive() +50
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +627

Client:

  <system.serviceModel>
     <bindings> 
        <basicHttpBinding> 
           <binding name="basicBinding"> 
              <security mode="TransportCredentialOnly">
                  <transport clientCredentialType="Windows" 
                             proxyCredentialType="Windows" realm="" />
                  <message clientCredentialType="UserName" 
                           algorithmSuite="Default" />
              </security> 
           </binding> 
        </basicHttpBinding> 
     </bindings> 
     <client>
         <endpoint 
             address="http://hrpaysservice/service1.svc" 
             binding="basicHttpBinding"
             bindingConfiguration="basicBinding" 
             contract="HRPaysService.IService1">
         </endpoint>
     </client>
  </system.serviceModel>

Server:

<system.serviceModel>
   <bindings> 
      <basicHttpBinding> 
         <binding name="basicBinding"> 
            <security mode="TransportCredentialOnly">
                <transport clientCredentialType="Windows" 
                           proxyCredentialType="Windows" realm="" />
                <message clientCredentialType="UserName" 
                         algorithmSuite="Default" />
            </security> 
         </binding> 
      </basicHttpBinding> 
   </bindings> 
   <client>
       <endpoint 
           address="http://hrpaysservice/service1.svc" 
           binding="basicHttpBinding"
           bindingConfiguration="basicBinding" 
           contract="HRPaysService.IService1">
       </endpoint>
</client>
</system.serviceModel>
4
is your client a Silverlight app by any chance?? Those work quite differently from a ASP.NET or Winforms/WPF app.marc_s

4 Answers

5
votes

I encountered the same error when I tried to access a WCF service hosted on IIS through adding a "Service Reference" to my Windows Forms application. But when the client hit a call for a service method, I got "UnAuthorized 401 exception". Here is my solution to this problem:

(1) I was using [wsHttpBinding] switch it to be [basicHttpBinding] as follows in the WCF service config file:

    <system.serviceModel>
      <bindings>
          <basicHttpBinding>
              <binding name="BasicHttpEndpointBinding">
                  <security mode="TransportCredentialOnly">
                      <transport clientCredentialType="Windows" />
                  </security>
              </binding>
          </basicHttpBinding>
      </bindings>
      <services>
      <service behaviorConfiguration="ServiceBehavior"   name="IService1">
          <endpoint address="" binding="basicHttpBinding"
            bindingConfiguration="BasicHttpEndpointBinding"
            name="BasicHttpEndpoint" contract="IService1">
              <identity>
                  <dns value="localhost" />
              </identity>
          </endpoint>
          <endpoint address="mex" binding="mexHttpBinding"
              contract="IMetadataExchange" />
      </service>
  </services>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
    multipleSiteBindingsEnabled="true" />

(2) Add a "Service Reference" from your client application and give it a name (we will use that name in the following step as "ProxyCalssName")

(3) adjust the app.config file of the client application to as follows:

<system.serviceModel>
    <client>
        <endpoint address="your service URL"
            binding="basicHttpBinding" bindingConfiguration="basic" contract="ProxyClassName.ServiceName"
            name="default" />
    </client>
    <bindings>
        <basicHttpBinding>
            <binding name="basic">
                <security mode="TransportCredentialOnly">
                    <transport clientCredentialType="Windows" proxyCredentialType="None"
                        realm="" />
                    <message clientCredentialType="UserName" algorithmSuite="Default" />
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>
</system.serviceModel>

(4) In the code behind of the client Application:

        ProxyClassName.MyServiceName srv = new ProxyClassName.MyServiceName("default");
 //default is the name of the endpoint in the app.config file as we did.
    srv.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

Good Luck, DigitalFox

1
votes

Client:

<system.serviceModel>
    <bindings>
        <wsHttpBinding>
                <binding name="WSHttpBinding_IService1" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                     bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
                    textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
                        <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                        <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="Message">
                        <transport clientCredentialType="Windows" proxyCredentialType="None"
                        realm="" />
                        <message clientCredentialType="Windows" negotiateServiceCredential="true"
                        algorithmSuite="Default" establishSecurityContext="true" />
                    </security>
                </binding>
        </wsHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://localhost:3097/Service1.svc" binding="wsHttpBinding"
            bindingConfiguration="WSHttpBinding_IService1" contract="HRPaysService.IService1"
            name="WSHttpBinding_IService1">
                <identity>
                    <dns value="localhost" />
                </identity>
        </endpoint>
    </client>
</system.serviceModel>

Server:

    <system.serviceModel>
        <bindings> 
        <basicHttpBinding> 
                    <binding name="basicBinding"> 
                    <security mode="TransportCredentialOnly"> 
                            <transport clientCredentialType="Windows"/> 
                    </security> 
                </binding> 
            </basicHttpBinding> 
        </bindings> 
    <services>
            <service behaviorConfiguration="basicBehavior" name="WcfService1.Service1"> 
                <endpoint address="" binding="basicHttpBinding" contract="WcfService1.IService1" bindingConfiguration="basicBinding" /> 
                <endpoint address="mex" binding="basicHttpBinding" contract="IMetadataExchange" bindingConfiguration="basicBinding" /> 
        </service>
    </services>
    <behaviors> 
            <serviceBehaviors> 
                <behavior name="basicBehavior"> 
                    <serviceMetadata httpGetEnabled="true" /> 
                </behavior> 
            </serviceBehaviors> 
        </behaviors> 
</system.serviceModel>

0
votes

Do you have a CrossDomain.xml document set up in your services web application? If not, create one with the following contents -

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>
0
votes

If the Virtual directory of WCF services is not configured for anonymous access, then the "mex" endpoint in should be removed.

You have posted 2 different sets of configs and there seems to be mismatch. Could you post the configs that is causing the error ?

Your first (top most) client config and the latest server config (without the mex part) should work.