In previous sections, we learned the basic concepts and theories about WCF security. Now, we will do some practical work. We will host a WCF service with Basic Authentication and then consume this service in a client application.
Since with Basic Authentication, username and password are transmitted in Base64 encoded text, SSL will be configured with this authentication mode to enhance the security of the service.
To keep the code simple and focus only on the security side of the WCF service, HelloWorldService
and HelloWorldClient
will be used for this practice.
First, we will set up a copy of HelloWorldService
with no authentication. We will enhance this service in the following sections to enable Basic Authentication and host it with HTTPS protocol.
To set up the service, follow these steps:
HelloWorld
solution ready.HostIISSecure
under the folder C:SOAwithWCFandLINQProjectsHelloWorld
.HostIIS
to this new folder.HelloWorldServiceSecure
, pointing to this new folder.http://localhost/HelloWorldServiceSecure/HelloWorldService.svc
You should see the WSDL description page of the HelloWorldService
service. Click on the link to make sure you can get the WSDL of this new service.
Basic Authentication is a feature of Internet Information Service. By default, the module is not installed. In this section, we will install and enable this feature.
Now the Basic
Authentication feature is enabled for the computer, but the IIS application is still not using it. We need to configure the HelloWorldServiceSecure
application to be authenticated with this feature.
Anonymous Authentication must be disabled because when negotiating with the server, most browsers will try Anonymous Authentication first.
Now if you
browse to the original service address, you will get an error The authentication schemes configured on the host ('Basic') do not allow those configured on the binding 'BasicHttpBinding' ('Anonymous'). This is because the HelloWorldServiceSecure
service is still configured to communicate using Anonymous Authentication. Next, we will modify the service configuration file to use Basic Authentication instead.
Before we modify the service configuration file, we need to do more on the IIS site. We will enable the HTTPS protocol for our website so that the client can communicate with our service via the HTTPS protocol. This is especially important for Basic Authentication, since with Basic Authentication, the username and password are transmitted in Base64 encoded text, which can be easily decoded to clear text.
To configure HTTPS for our service, follow these steps:
MyTestCert
for the certificate and click on the OK button to close the dialog window.HelloWorldServiceSecure
application. By default, this should be Default Web Site. Make sure you select the website, not the HelloWorldServiceSecure application, as binding settings apply to the whole website.In your production environment, you should create a signed certificate from a Certificate Authority like VeriSign. And in development time, if SSL brings too much overhead, you can host your service without SSL. It is recommended for security purposes to turn it on when the service is deployed to the production environment.
Now IIS is
configured to use Basic Authentication, but the service is still not. We need to configure the HelloWorldService
service to be authenticated with Basic Authentication.
HelloWorld
solution.HostIISSecure
to the solution.C:SOAwithWCFandLINQProjectsHelloWorldHostIISSecureweb.config
file to the new solution folder.bin
under the HostIISSecure
solution folder.C:SOAwithWCFandLINQProjectsHelloWorldHostIISSecurein
folder to this new solution folder.HelloWorldService
project, and add the following statements to the post-built event of the project. This will make sure the HelloWorldServiceSecure
application will always have the latest service binary files as other hosting applications.xcopy "$(AssemblyName).dll" "C:SOAwithWCFandLINQProjectsHelloWorldHostIISSecurein" /Y xcopy "$(AssemblyName).pdb" "C:SOAwithWCFandLINQProjectsHelloWorldHostIISSecurein" /Y
web.config
file and make the following changes to this file:a. Change the service metadata attribute from httpGetEnabled
to httpsGetEnabled
.
b. Add the following services
node just after the behaviors
node. This will define an endpoint with our specific binding configuration:
<services> <service name="MyWCFServices.HelloWorldService"> <endpoint address="" binding="basicHttpBinding" contract="MyWCFServices.IHelloWorldService" bindingConfiguration="secureHttpBinding"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services>
c. Add the following bindings
node just after the new node that we just added. This will define the binding with Basic Authentication:
<bindings> <basicHttpBinding> <binding name ="secureHttpBinding"> <security mode="Transport"> <transport clientCredentialType="Basic" /> </security> </binding> </basicHttpBinding> </bindings>
d. Now the system.serviceModel
node should have four child nodes: serviceHostingEnvironment
(existing node), behaviors
(existing node), services
(new node), and bindings
(new node).
e. Save your web.config
file.
In your
web.config
file, the system.serviceModel
node should be as follows:
<system.serviceModel> <serviceHostingEnvironment > <serviceActivations> <add factory="System.ServiceModel.Activation.ServiceHostFactory" relativeAddress="./HelloWorldService.svc" service="MyWCFServices.HelloWorldService"/> </serviceActivations> </serviceHostingEnvironment> <behaviors> <serviceBehaviors> <behavior> <serviceMetadata httpsGetEnabled="true"/> </behavior> </serviceBehaviors> </behaviors> <services> <service name="MyWCFServices.HelloWorldService"> <endpoint address="" binding="basicHttpBinding" contract="MyWCFServices.IHelloWorldService" bindingConfiguration="secureHttpBinding"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> <bindings> <basicHttpBinding> <binding name ="secureHttpBinding"> <security mode="Transport"> <transport clientCredentialType="Basic" /> </security> </binding> </basicHttpBinding> </bindings> </system.serviceModel>
The service is now ready. You can go to https://[your_pc_name]/HelloWorldServiceSecure/HelloWorldService.svc
and see the WSDL of the service.
You will be prompted to enter your credentials. You should then enter your Windows login credentials here.
If you go to https://localhost/HelloWorldServiceSecure/HelloWorldService.svc
(using localhost instead of your PC name in the URL), you will get a warning that The security certificate presented by this website was issued for a different website's address. You can ignore this warning and still see the WSDL of the service. However, in a following section, when you add the service reference, you must use your PC name, instead of localhost.
On the service WSDL page, you should see the following XML node:
<http:BasicAuthentication xmlns:http="http://schemas.microsoft.com/ws/06/2004/policy/http"/>
This means the service is now secured by Basic Authentication.
To test the secured service, we need a test client. In this section, we will create a console application to test the secured service.
HelloWorldClientSecure
to the solution.https://[your_pc_name]/HelloWorldServiceSecure/HelloWorldService.svc
as the address and HelloWorldServiceRef
as the namespace. You will be prompted to enter your credentials. Enter your Windows login credentials here.If you get an error saying An error (Details) occurred while attempting to find service and the error details are Metadata contains a reference that cannot be resolved, you may need to give your IIS identity proper access rights to your Windows emp
directory.
You may be prompted to enter your credentials up to three times while Visual Studio is trying to discover or enumerate all the services for the desired name on your machine. In my environment it was trying to discover the service at three places—HelloWorldService.svc/_vti_bin/ListData.svc/$metadata
, HelloWorldService.svc/$metadata
, and HelloWorldService.svc
.
If you are prompted more than three times for your credentials and you still cannot add the service reference, make sure you are using the correct URL for the service. You may need to replace localhost with the system name of your PC or you may need to include your domain name in the URL. This is because the host part of the URL must match the Issued to
part of the self-signed certificate. You can open your certificate (from IIS manager) to see the exact party name that this certificate is issued to, and use this in your URL. In the following screenshot, the computer is not in a domain, so Issued to in this case is the same as the computer name. If it is within a domain, the Issued to field should look something like MyLaptop.domain_name.com
. In this case, you should use MyLaptop.domain_name.com
in the URL.
If your computer is within a domain, you also need to make sure you have set your default domain and realm in your bindings settings. You can refer to the previous section, Configuring the HTTPS protocol, for more details.
Program.cs
file and add the following code to the Main
method (remember to replace the user's credentials with your own one):var client = new HelloWorldServiceRef.HelloWorldServiceClient(); client.ClientCredentials.UserName.UserName = "your_user_name"; client.ClientCredentials.UserName.Password = "your_password"; Console.WriteLine(client.GetMessage("Basic Authentication caller"));
HelloWorldClientSecure
project as the startup project and run the program. You should get an output that looks similar to the one shown in the following screenshot: