WCF : WS-Security Hell
Lastly, I had to build a client to consume a web service. This task seems a bit trivial at first, but the service required an authentication mechanism based on WS-Security. Nothing really complex, the only requirement was to send a username and a password in plain text.
As usual when I build this kind of application, I used Visual Studio wizard to create the proxy to the web service, and after some tweaking in the app.config file, my application was ready to consume this web service !
Unfortunately, there is a security behavior in WCF that prevent from using plain text message based authentication over unsecure transport : i.e. you cannot send a clear password if you are using HTTP as the transport layer instead of HTTPS. That would have been nice of Microsoft to give us a way to override this mechanism easily, but they didn't (and they didn't bother to add an explicit error message in the thrown exception...)
So, if you want to send a plain-text username and password using WCF, the only way I found was to stuff the corresponding soap header when sending the request.
Here is my code, use it at your own risks...
public class SecurityBehavior : MessageHeader, IClientMessageInspector, IEndpointBehavior { private string m_sUserName; private string m_sPassword; public override string Name { get { return "wsse:Security"; } } public override string Namespace { get { return ""; } } public string UserName { get { return m_sUserName; } set { m_sUserName = value; } } public string Password { get { return m_sPassword; } set { m_sPassword = value; } } public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { } public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) { request.Headers.Add(this); return null; } public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add((IClientMessageInspector)this); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { } protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) { writer.WriteAttributeString("xmlns", "wsse", null, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); writer.WriteStartElement("wsse:UsernameToken"); writer.WriteElementString("wsse:Username", m_sUserName); writer.WriteStartElement("wsse:Password"); writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"); writer.WriteValue(m_sPassword); writer.WriteEndElement(); //wsse:Password writer.WriteEndElement(); //wsse:UsernameToken } }

What's the trick....
So I added a behavior like this, but the client still throws an exception, complaining about the authentication type.
{"BasicHttp binding requires that BasicHttpBinding.Security.Message.ClientCredentialType be equivalent to the BasicHttpMessageCredentialType.Certificate credential type for secure messages. Select Transport or TransportWithMessageCredential security for UserName credentials."} System.Exception {System.InvalidOperationException}
There is a hack to put in the
There is a hack to put in the config file, but I can't remember exactly. Unfortunately, I lost the files associated with this project...