Maintain security configuration after changing WCF service endpoint programmatically

By : Bardo
Source: Stackoverflow.com
Question!

I've an ASP.NET MVC application where we use several external services.

We define the endpoint to each of the services in web.config in this fashion:

<endpoint address="http://tempuri.org/myExternalServiceEndPointAddress"
    binding="basicHttpBinding" bindingConfiguration="myExternalServiceBinding" contract="myExternalServiceContract" name="MyExternalServiceName">
    <headers>
      <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <wsse:UsernameToken>
          <wsse:Username>myUsername</wsse:Username>
          <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">myPassword</wsse:Password>
        </wsse:UsernameToken>
      </wsse:Security>
    </headers>
  </endpoint>

Now, when switching to production environment, we had to discover the actual endpoint url through a specific service, as they can change without warning. Once the new endpoint is found, we change the endpoint url before executing any method call with this:

myserviceClientObject.Endpoint.Address = new EndpointAddress(newEndpointUrl);

Once we make this, the method call is correctly addressed at newEndpointUrl, but the call has lost header nodes where validation is included.

We've tried to add authentication programatically through

myServiceClientObject.ClientCredentials.Username.Username = myUsername;
myServiceClientObject.ClientCredentials.Username.Password = myPassword;

However, the service doesn't seems to recognize this authentication (while previous one was working perfectly until we changed the endpoint).

Any insight or suggestion will be much appreciated.

By : Bardo


Answers

I've finally solved this problem.

What we did in the end was to wire into the WCF pipeline an object with the desired headers.

To do this we create a class that added a client behavior in the service and another one which defined the behavior itself, where the injection was made.

Here is the code:

Class that adds the behavior

public class EndpointAddCredentials : IEndpointBehavior {

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) {
        clientRuntime.MessageInspectors.Add(new SimpleMessageInspector());
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }

    public void Validate(ServiceEndpoint endpoint) { }

}

Class that defines and applies it before sending requests

public class SimpleMessageInspector : IClientMessageInspector, IDispatchMessageInspector {

    public void AfterReceiveReply(ref Message reply, object correlationState) {
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel) {


        UsernameToken authentication = new UsernameToken(remoteServiceUsername, remoteServicePassword, PasswordOption.SendPlainText); //Plain text is server requirement, we cannot do anything

        var webUserHeader = MessageHeader.CreateHeader("Security", 
            "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", authentication.GetXml(new XmlDocument()));
        request.Headers.Add(webUserHeader);

        return null;
    }

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) {
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState) {
    }
}

Then, after service redirection, we just have to add the behavior to our client object

serviceObject.Endpoint.Behaviors.Add(new EndpointAddCredentials()); 

With this the requests to the server included the security definition we were looking for and the server accepted them.

By : Bardo


This video can help you solving your question :)
By: admin