The task in hand can be stated as follows: Create a WCF service hosted in IIS7 over HTTPS with a custom user and password validator. Use Transport mode, and no message encryption. Sounds easy, right?
It is a common task to design a service secured by HTTPS with the ability to use a custom validator (e.g., against SQL or other custom authentication scheme). What is wanted is a simple secure service with very little coding.
There are tons of articles describing custom username validation with Transport or Message security mode binding over the net (a large collection is available on CodePlex, or just search in Google for "WCF username transport"), but when dealing with a production environment, I found so much problems, so I though it would be useful to mention them in this article.
I suppose you already have some experience with WCF; you will not found here a complete example, rather tips on how to set it all up to work correctly.
Building a service secured with HTTPS is easy. In the following documentation and examples, we declare the contract, implementation, and configuration section for our service:
For contracts, we define IDC.cs as follows:
public interface IDC
bool DoSomethingSecurely(string sParameter);
For simplicity, we omit data contracts.
The implementation of the service contract is in DCService.svc:
public class DCService : IDC
public bool DoSomethingSecurely(string sParam)
return sParam == "isthissafeservice?";
public class UNValidator : UserNamePasswordValidator
public override void Validate(string userName, string password)
if (userName == "test" && password == "the best")
throw new System.IdentityModel.Tokens.SecurityTokenException(
"Unknown Username or Password");
Notice that we also defined a class
UNValidator which will be used later as a custom username and password validator.
Now we have to define the configuration of the service. We will discuss the individual parts first; notice that only the
<diagnostics> sections of your config file are presented here!
<endpoint address="mex" binding="mexHttpsBinding"
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<add name="xml" />
<add name="xml" />
<trace autoflush="true" indentsize="4"/>
The most problematic things are hidden in the configuration of the service, as expected...
WsHttpBinding with security mode
TransportWithMessageCredentials. You might ask, why not
Transport. The answer is, if we want to use a simple custom username validator under IIS, we must use
TransportWithMessageCredentials, even it has a large overhead. Under IIS, a custom validator will not work with
Transport security. At least, I couldn't get it to work, and it is also mentioned here.
Be aware to set these large enough, or you will get strange errors or even time outs; the service will tell you nothing about the small message size.
Even if we use HTTPS (a.k.a. Transport), security is achieved with the SOAP message authentication mechanism, so we have to set the
<security> element with the
<message> child specified. We set
custom. By this, we enable the use of the custom username and password validator.
Do not forget to enable this during development, or you probably will not be able to get the service metadata when building the client. If enabled, you can also browse and test the service mex endpoint from the browser. Disable this for production.
These elements allow you to specify that the custom user name and password validator will be implemented by our previously defined class.
customUserNamePasswordValidatorType defines the type of the class implementing the validator. The syntax is, as in the other cases: "classname with namespace, assembly name".
These sections are useful when you are not able to get it to work and you don't know what to do. Activate diagnostics make client requests and then open the trace files - Microsoft Trace viewer will open the files.
So, this is our safe service. We put it on IIS, and 99%, it will not work. We have to setup IIS carefully too.
You have to take note of the following points to do, or check these things for a given environment:
- Create website
- Create separate application pool (production)
- Enable WCF activation in server settings (not IIS, it's in the Turn On/Off system features on 2008 Server, or W7 if you're developing)
- Enable the HTTPS protocol
- Add HTTPS binding
- In production, remove HTTP binding
- Enable anonymous authentication, disable all others
- Create server certificate, add one to the site (for testing, follow the sample on CodePlex)
This should be all. After that, test your service in the browser. Maybe somebody could point out any other pitfalls you might have.
This is easy. In the client, you add the service reference; you can use disco, or enter the address as I did (in my case, http://localhost/DocumentCenterWCF/SafeService.svc).
But be aware, you must temporarily allow
httpsGetEnabled in the service config to be able to get meta-data.
Once you have your service reference added, you may want to start to use the client. Use the standard approaches for constructing the client, setting the credentials, binding and endpoints, and for calling methods repeatedly. You might get strange time-out errors (channel time out or something like that). The trace will show some errors, but I bet you will not be able to find out any more. The only thing that pointed me to solution was something about object activation. Activation needs deactivation...
The problem is you have to explicitly call the
Close method on the
ChannelFactory of the client. A smarter and more robust solution is to use
using, because the client implements
So, in this case, I use something like this, which I found useful in a few cases of creating WCF clients:
protected DCService.DCClient getWCF()
EndpointAddress ep = new EndpointAddress(new Uri(
WSHttpBinding bind = new WSHttpBinding(
bind.ReceiveTimeout = new TimeSpan(0, 0, 10);
bind.SendTimeout = new TimeSpan(0, 0, 10);
bind.BypassProxyOnLocal = false; bind.UseDefaultWebProxy = true; bind.Security.Message.ClientCredentialType =
DCService.DCClient cli = new DCService.DCClient(bind, ep);
cli.ClientCredentials.UserName.UserName = WS_user;
cli.ClientCredentials.UserName.Password = WS_pwd;
public bool DoSomething(string something)
using (var cli = getWCF())
Please note, this is only an example; the proxy and endpoint addresses should be configurable, but I like to configure client things in the code using my own settings, and this example should illustrates that I have put some things as URL hard-coded.
Final thing - you will meet with certificate errors if you are developing and have not installed a trusted certificate. During development, I use a simple certificate workaround, but be aware to make the proper check for the certificate in production to make the service secure!
ServicePointManager.ServerCertificateValidationCallback = new
public static bool ValidateServerCertificate(
WCF is nice, reliable, and efficient. But you have to think a lot all the time and try to understand the whole framework. Copy/Paste in the case of WCF is not always the good choice.