I’m working on a project where a service hosted elsewhere requires Windows authentication.  As a consultant, my computer is not on the customer’s domain, so, using my logged-on Windows credentials won’t do any good.  And, doing nothing results in the following exception:  “The caller was not authenticated by the service.” and inner exception is “The request for security token could not be satisfied because authentication failed.”

 

To make this work, I need to create instance of NetworkCredential class and pass it on through the client proxy prior to making any calls to the service, e.g.

((System.ServiceModel.ClientBase<YourNamesapce.IYourService>)(client)).ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential("login", "password", "domain");

 

Since the project is going to be used by domain users, and the situation described above won’t be an issue, I’m one of the few consultants that have to deal with this, and, of course, putting the line above in the code is not an option.  As an alternative, and, in my opinion, a more elegant solution, I wrote custom endpoint behavior, so, the only two things I need to do now is:

  1.  Make sure my WCF custom behavior assembly is in the bin file of the client
  2.  Update client side configuration file as follows:

      a)     Add behaviorExtension element

    <extensions>

        <behaviorExtensions>

            <add name="windowsClientCredentials" type="WCFExtensions.WindowsClientCredentialsConfig, WCFExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

        </behaviorExtensions>

    </extensions>

      b). Add windowsClientCredentials element to the behavior used by the endpoint, e.g.

    <behaviors>

        <endpointBehaviors>

            <behavior name="NewBehavior0">

                <windowsClientCredentials user="x" password="y" domain="z" />

            </behavior>

        </endpointBehaviors>

    </behaviors>

 

NOTE:  I have not added encryption of the credentials in the config file.  If you choose to use it in an enterprise application, I strongly suggest you secure the login settings.

Below is the code for the custom behavior endpoint (just create a Windows class library project, add a class with the code below and compile.

 

using System;

using System.Configuration;

using System.Linq;

using System.ServiceModel.Configuration;

using System.ServiceModel.Description;

using System.ServiceModel.Dispatcher;

 

namespace WCFExtensions

{

    public class WindowsClientCredentialsConfig : BehaviorExtensionElement

    {

        protected override object CreateBehavior()

        {

            return new WindowsClientCredentialsBehavior(this.User, this.Password, this.Domain);

        }

 

        public override Type BehaviorType

        {

            get { return typeof(WindowsClientCredentialsBehavior); }

        }

 

        [ConfigurationProperty("user", IsRequired = true)]

        public string User

        {

            get { return (string)this["user"]; }

            set { this["user"] = value; }

        }

 

        [ConfigurationProperty("password", IsRequired = true)]

        public string Password

        {

            get { return (string)this["password"]; }

            set { this["password"] = value; }

        }

 

        [ConfigurationProperty("domain", IsRequired = true)]

        public string Domain

        {

            get { return (string)this["domain"]; }

            set { this["domain"] = value; }

        }

    }

 

 

    public class WindowsClientCredentialsBehavior : IEndpointBehavior

    {

        private string _user, _password, _domain;

 

        public WindowsClientCredentialsBehavior(string user, string password, string domain)

        {

            _user = user;

            _password = password;

            _domain = domain;

        }

 

        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)

        {

            ClientCredentials clientCredentials = (from item in bindingParameters where item is ClientCredentials select item).FirstOrDefault() as ClientCredentials;

            if (clientCredentials == null)

            {

                clientCredentials = new ClientCredentials();

                bindingParameters.Add(clientCredentials);

            }

 

            clientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(_user, _password, _domain);

 

        }

 

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)

        {

 

        }

 

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)

        {

        }

 

        public void Validate(ServiceEndpoint endpoint)

        {

            ClientCredentials clientCredentials = (from item in endpoint.Behaviors where item is ClientCredentials select item).FirstOrDefault() as ClientCredentials;

            if (clientCredentials != null)

                endpoint.Behaviors.Remove(clientCredentials);

        }

    }

}