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…
{
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
}
}