WCF Service Using Username/Password Authentication

This example WCF application will contain these features:

  1. Force the client to pass a username and password prior to calling any of the services operations
  2. The service will be exposed over a net.tcp endpoint
  3. The net.tcp binding will implement TransportWithMessageCredentials security
  4. The endpoint behavior will allow httpGet for it’s Metadata
  5. The endpoint behavior will expose an SSL Certificate (used for the TransportWithMessageCredentials)
  6. The endpoint behavior will use a custom MembershipProvider for authenticating the username and password that was sent from the client
  7. The host will be IIS 7
  8. The client will be a Windows Form

The purpose of this WCF example is to show you how to use Visual Studio 2010 and IIS 7 to create the hooks that are necessary for supporting the features that are mentioned in the feature list above. It may be worth mentioning that this example is just one of many different ways to set up the service.

Download the source files here

The WCF Service

Using Visual Studio 2010, create a “WCF Service Library” project.

The default code generated by VS2010 creates two contracts:

  1. [ServiceContract]: Defines the interface of the operations  that will be exposed to the clients
  2. [DataContract]: Defines a custom data type that will be used by the client and service. The data contract can be your own classes that do whatever it is that you want to do – Just make sure you decorate your classes with the [DataContract] and [DataMember] attributes

The service that VS2010 generated is stub code that will be removed/replaced in a real production application. However, the stub code will be sufficient to demonstrate the plumbing that is necessary for client credential authentication between the client and service that we want to demonstrate in this example.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WcfServiceLibrary
{
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);

[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
}

[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";

[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}

[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
}

The WCF Host

This example will use IIS 7 to host the WcfServiceLibrary.Service1 service.

In the same Solution as the WcfServiceLibrary.Service1, add a new web application to IIS by right-clicking the Solution and choosing “Add > New Web Site”.

Specify the Web Location to http://localhost/WcfServiceHost

Add a reference to the web application that points to the WcfServiceLibary project. After you add the reference to our service assembly, you will notice the web application now has a Bin directory that contains the .dll of our service. If you don’t see the Bin directory, run a Build on the web application.

By default, VS2010 will add a couple of files that define a WCF service (IService.cs and Service.cs) and will point to that service in its .svc file. Since we have already created our own service and we have added a reference to our service, we have no need for the auto-generated service – so, let’s remove that service and then update the .svc file so that it points to our service.

Delete the IService1.cs and Service1.cs files that are located in the App_Code directory of the website ((this is that WCF service that VS2010 auto-created when we added the website).

Update the Service.svc file so that it points to the reference to our service assembly (as opposed to the service that was auto-generated when the web application was created).

Old Service.svc content…
<@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>

Updated Service.svc content…
<%@ ServiceHost Service="WcfServiceLibrary.Service1" %>

Notice how the Service.svc file points to the assembly reference WcfServiceLibrary.Service1 (this is the hook for configuring the web application to use the WcfServiceLibrary.Service1.dll that is located in its Bin directory).

Reviewing what we have done so far

At this point we have a project that defines a WCF Service and have created a web application that references that service and will host that service. We now need to configure IIS (our host) to support the bindings, protocols, and security that we want to implement.

Configuring IIS (our WCF Host)

Open IIS Manager…

  1. From the Start menu, click Run.
  2. In the Open box, type inetmgr, then click OK.

Add the Net.Tcp Binding

Right-click on the Default Web Site then choose “Edit Bindings…“. Add a net.tcp binding type and set the Binding Information to 8081:*

What did we just do and why did we do it?
The Default Web Site is the web site that contains our WCF application. Since our WCF application will be using the net.tcp binding type, we need to add the Net.Tcp binding to the Default Web Site so that it will listen for net.tcp requests. By adding the Net.Tcp binding to the web site, we are essentially defining the port number that IIS will listen on for net.tcp protocol requests (i.e. requests that will be made from our WCF client). This is no different than defining the HTTP binding for the Default Web Site in the sense that requests for web pages on the HTTP transfer do so on port 80 (by default). In other words, just as setting the HTTP Binding configures IIS to listen for HTTP requests (browser requests) on port 80, adding the Net.TCP Binding configures IIS to listen for Net.Tcp requests on the specified port.

If we do not configure the net.tcp binding on our IIS Host, exceptions will be thrown.
On the WCF client side, an exception will be similar to:

Could not connect to net.tcp://localhost:8081/WcfServiceHost/Service1.svc. The connection attempt lasted for a time span of 00:00:02.1720000. TCP error code 10061: No connection could be made because the target machine actively refused it 127.0.0.1:8081.

Add the Net.Tcp Protocol

In the previous step we added the Net.Tcp binding to the web site that is hosting our WCF application. Now that the net.tcp binding is added and configured (i.e. IIS is listening for net.tcp protocol requests on port 8081), we need to configure our WCF application to use/enable the net.tcp protocolBy default, IIS only enables the HTTP protocol for its folders, virtual directories, and applications.

In IIS Manager, right-click on the WcfServiceHost application and choose “Manage Application > Advanced Settings…“. Then, add the net.tcp protocol to the Behavior section for Enabled Protocols.

What did we just do and why did we do it?
We have added the WcfServiceHost application to the Default Web Site in an effort to have IIS host our WCF Service. Since our WCF Service will communicate with clients over the net.tcp protocol, we needed to explicitly tell IIS that its WcfServiceHost application will be using the Net.Tcp protocol – i.e. by default, the only protocol that IIS enables for its child applications and folders is the HTTP protocol.

If we do not configure the WcfServiceHost application to enable the Net.Tcp protocol, our WCF Client will receive an exception error similar to:

The message could not be dispatched because the service at the endpoint address ‘net.tcp://localhost:8081/WcfServiceHost/Service1.svc’ is unavailable for the protocol of the address.

Pause for Clarification

Before we move on, I want to clarify the concept of a web application vs. a web site and a binding vs. a protocol.

Web Application vs. Web Site
IIS can support one or more Web Site’s. Each Web Site can contain one or more Virtual Directories (or Folders) and one or more Applications. Out of the box, IIS contains exactly one web site that is named Default Web Site. To the Default Web Site, you can add as many Virtual Directories and Applications that you wish. Technically speaking, the collection of folders, virtual directories, and applications are considered to be the “Web Site”. In short, the Web Site is the host (or the container) of the applications (and folders) that it contains.

A Web Application is a grouping of files (and binaries) that deliver content or provides one or more services over protocols, such as HTTP, HTTPS, and Net.Tcp. When you create or add an application in IIS, the application’s path becomes part of the site’s URL (i.e. http://localhost/WcfServiceHost). For more info, check out Understanding Sites, Applications, and Virtual Directories on IIS 7 and Understanding IIS Bindings, Websites, Virtual Directories, and lastly Application Pools.

A point to note here is that the Default Web Site has configuration properties that affect all of its child folders and applications. Likewise, the folders and applications have configuration properties that affect only that folder or application. So, by enabling the Net.Tcp protocol to our WcfServiceHost application, we have only enabled that protocol for that application.

Binding vs. Protocol
Conceptually speaking, for this example, a binding associates a Protocol (HTTP, HTTPS, Net.Tcp) with a Port Number (:80, :443, :8081) that lives at the Host Address of our Default Web Site (localhost). So, I suppose, a binding inherently turns out to be the same thing as the URL of a site – as in net.tcp://localhost:8081 – That’s our binding for Net.Tcp. The URI to our WCF application is at net.tcp://localhost:8081/WcfServiceHost.

The protocol, on the other hand, is

An agreed-upon format for transmitting data between two devices [ref]

Putting all this together in context of our WCF example, we had to configure IIS to listen for Net.Tcp requests on port 8081 -i.e. any application that uses the Net.Tcp protocol will live at net.tcp://localhost:8081. And then, we configured the WCF application to allow or support the Net.Tcp protocol – meaning that the WCF application will transmit data using the Net.Tcp format and, therefore, only clients that understand the Net.Tcp protocol will have any success communicating with the WCF application.

Create a Self-Signed SSL Certificate and Bind to Port

In order to use the TransportWithMessageCredential security mode when a client passes its Username and Password to the service, WCF requires that we use some form of security. In this example we will use an SSL Certificate – and, to keep things easy (and free), we will use a Self-Signed Certificate. After we create the cert, we will assign the cert to an arbitrary port number.

Keep in mind that using an Self-Signed cert is only good for development purposes. In a production environment you will need to purchase an SSL Certificate from a Certificate Authority (CA). Then you will need to configure IIS by adding an HTTPS binding and associating the certificate with the HTTP protocol. Finally, you will need to update the Web.config file to use the Thumbprint of your “official” SSL Certificate.

Create the SSL Certificate

  1. Open IIS Manager and open Server Certificates
  2. Click the “Create Self-Signed Certificates….” link
  3. Enter a name for the certificate
  4. Open the certificate and copy the Thumbprint

Assign SSL Certificate to a Port

Open a command prompt and enter the following command..

netsh http add sslcert ipport=0.0.0.0:5150 certhash=e2209530871c83319c817b0d7da47620d54d0ba6 appid={B8CA0613-6250-4DDB-A693-74B8678C2DF6}

  • The ipport 5150 is an arbitrary value. You can use any port number that you know is not being used.
  • The certhash is the Thumbprint of the certificate – Note the spaces are removed!
  • The appid is a GUID in Registry Format. (You can use Visual Studio to create the GUID by going to Tools > Create GUID)

WCF Client – Windows Form

The consumer of this example WCF Service will be a Windows Form. In the Windows Form project we will create a proxy for our example WCF service, then we will invoke the service operations by, simply, clicking a Button.

Add a new Windows Form project to the solution

Add a Service Reference to the WindowsFormApplication1 project

Adding the Service Reference using Visual Studio will generate a handful of files known collectively as the “service proxy”. It’s worth the time to take a gander at all the files that were generated – especially the Reference.cs and Service1.wsdl. To see the Service Reference files you will need to turn on “Show All Files”

In addition to the proxy files, an “app.config” file was generated.
The interesting section of the app.config file that you should be familiar with is the <client> section which looks similar to this:

        <client>
            <endpoint address="http://localhost/WcfServiceHost/Service1.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
                contract="ServiceReference1.IService1" name="BasicHttpBinding_IService1" />
            <endpoint address="net.tcp://dv9500t:8081/WcfServiceHost/Service1.svc"
                binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IService1"
                contract="ServiceReference1.IService1" name="NetTcpBinding_IService1">
                <identity>
                    <servicePrincipalName value="host/DV9500T" />
                </identity>
            </endpoint>
        </client>

Notice the two endpoints were created. For this example we are mostly interested in the endpoint that uses the net.tcp protocol. Notice how the address points to the same port (8081) that we defined earlier when added the net.tcp binding in IIS. This is no coincidence! This is the endpoint that we will be using throughout the example.

Add some UI to the client form…

Add event handlers for the Button click events. The handlers will instantiate the proxy and invoke the service operations. Finally, the client will display the response that it received from the service.

        private void btnGetData_Click(object sender, EventArgs e)
        {
            //Since more than one endpoint configuration exists in the app.config,
            //specify the configurationBinding that we want to use.
            using (ServiceReference1.Service1Client proxy =
                new ServiceReference1.Service1Client("NetTcpBinding_IService1"))
            {
                string response = proxy.GetData(Convert.ToInt32(this.nudGetDataInput.Value));
                this.txtGetDataResponse.Text = response;
            }
        }

        private void btnGetDataContract_Click(object sender, EventArgs e)
        {
            using (ServiceReference1.Service1Client proxy =
                new ServiceReference1.Service1Client("NetTcpBinding_IService1"))
            {
                //Create a CompositeType object, then pass it to the service
                ServiceReference1.CompositeType ct = new ServiceReference1.CompositeType();
                ct.BoolValue = this.radGetDataContractTrueInput.Checked ? true : false;
                ct.StringValue = this.txtGetDataContractInput.Text;
                ServiceReference1.CompositeType ctResponse = proxy.GetDataUsingDataContract(ct);

                //Fill the form with the values of the CompositeType
                //object that was returned from the service
                this.txtGetDataContractResponse.Text = ctResponse.StringValue;
                this.radGetDataContractTrueOutput.Checked = ctResponse.BoolValue;
                this.radGetDataContractFalseOutput.Checked =
                    !this.radGetDataContractTrueOutput.Checked;
            }
        }

At this point you can run the client and invoke the service operations.

It’s now time to implement the hooks that will require the client to pass a username and password in order to use the service operations.

Client Authentication

To implement client authentication we will:

  1. Update the hosts Web.config file
  2. Add a custom MembershipProvider class to the host that will authenticate the client credentials
  3. Update the service proxy on the client (update the Service Reference)
  4. Update the client form to allow the user to enter a username and password
  5. Update the client code to pass the username/password to the service

Update Web.config

Create a netTcpBinding
Define a binding that will use the TransportWithMessageCredential security mode and use the UserName clientCredentialType that the endpoint will use.

Add the following configuration to the <system.serviceModel> section of the Web.config file:

    <bindings>
      <netTcpBinding>
          <binding name="Tcp_TransportWithMessageCredentialBinding">
         <em> <security mode="TransportWithMessageCredential">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </netTcpBinding>
    </bindings>

Define a service behavior that allows the metadata exchange to occur over HTTP, specifies the Self-Signed cert that will be used for the Message Credential, and specifies that a MembershipProvider will be used for the username/password validation.

Add the following configuration to the <system.serviceModel> section of the Web.config file:

    <behaviors>
      <serviceBehaviors>
        <behavior name="CertAndAuthenticationBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceCredentials>
           <serviceCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" findValue="e2 20 95 30 87 1c 83 31 9c 81 7b 0d 7d a4 76 20 d5 4d 0b a6"/>
           <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="CustomMembershipProvider"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>

Notice the findValue (of the serviceCertificate element) value matches the Thumbprint of the Self-Signed Certificate that we created earlier (spaced included!).

Define the custom MembershipProvider that we want the CertAndAuthenticationBehavior behavior to use when doing the authentication.

Add the following configuration to the <configuration> section of the Web.config file:

<system.web>
    <membership defaultProvider="CustomMembershipProvider">
      <providers>
        <add name="CustomMembershipProvider" type="ServiceMembershipProvider" writeExceptionsToEventLog="true" applicationName="/"/>
      </providers>
    </membership>
  </system.web>

We will create the custom Membership Provider (ServiceMembershipProvider) immediately after we finish updating the Web.config file.

Specify the service and the behavior it will adhere to.

Add the following configuration to the <system.ServiceModel> section of the Web.config file:

    <services>
      <service name="WcfServiceLibrary.Service1"
               behaviorConfiguration="CertAndAuthenticationBehavior">
      </service>
    </services>

Define the net.tcp endpoint for the service. The endpoint will specify the location of the service, the type of binding to use, and the configuration of the binding.
Add the following configuration to the <service> section that you added in the last step.

<endpoint address="net.tcp://localhost:8081/WcfServiceHost/Service1.svc"
                  binding="netTcpBinding"
                  bindingConfiguration="Tcp_TransportWithMessageCredentialBinding"
                  contract="WcfServiceLibrary.IService1"/>

Here’s the complete Web.config file – Your .config file may differ slightly, but keep in mind that this Web.config is an example of the bare essentials that are necessary to implement Username/Password authentication over a secure transport (net.tcp) and encrypted messaging (Message tranfer):

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="WcfServiceLibrary.Service1"
               behaviorConfiguration="CertAndAuthenticationBehavior">
        <endpoint address="net.tcp://localhost:8081/WcfServiceHost/Service1.svc"
                  binding="netTcpBinding"
                  bindingConfiguration="Tcp_TransportWithMessageCredentialBinding"
                  contract="WcfServiceLibrary.IService1"/>
      </service>
    </services>
    <bindings>
      <netTcpBinding>
        <binding name="Tcp_TransportWithMessageCredentialBinding">
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="CertAndAuthenticationBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceCredentials>
            <serviceCertificate storeLocation="LocalMachine"
                                storeName="My"
                                x509FindType="FindByThumbprint"
                                findValue="e2 20 95 30 87 1c 83 31 9c 81 7b 0d 7d a4 76 20 d5 4d 0b a6"/>
            <userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
                                    membershipProviderName="CustomMembershipProvider"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.web>
    <membership defaultProvider="CustomMembershipProvider">
      <providers>
        <add name="CustomMembershipProvider"
             type="ServiceMembershipProvider"
             writeExceptionsToEventLog="true"
             applicationName="/"/>
      </providers>
    </membership>
  </system.web>
</configuration>

Before we can rebuild/compile the solution, we need to create the Membership provider that is specified in the <providers> section of the Web.config file.

Custom MembershipProvider

In Visual Studio’s Solution Explorer, add a new class to the Web Host’s App_Code folder and name it ServiceMembershipProvider.cs.
Make sure the following assembly references are added to your project:

  • System.Configuration
  • System.Web
  • System.Web.ApplicationServices

Add a using directive to System.Web.Security, then inherit the System.Web.Security.MembershipProvider class.

The custom membership provider class will look something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.ApplicationServices;
public class ServiceMembershipProvider : MembershipProvider
{
	....

Implement the MembershipProvider class

Add the following code to the ValidateUser() method:

    public override bool ValidateUser(string username, string password)
    {
        bool validated = false;
        if ((username.Length > 3 && username.Contains("wcf")) && (password.Length > 6 && password.Contains("pass")))
            validated = true;

        return (validated);
    }

The code above simulates an authenticator for the username and password. You will want to replace the simulation with a call to your assembly that does real validation.

Now rebuild the solution and update the client’s Service Reference.
The client’s app.config will look something like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <netTcpBinding>
                <binding name="NetTcpBinding_IService1" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
                    hostNameComparisonMode="StrongWildcard" listenBacklog="10"
                    maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"
                    maxReceivedMessageSize="65536">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="TransportWithMessageCredential">
                        <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
                        <message clientCredentialType="UserName" />
                    </security>
                </binding>
            </netTcpBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://localhost:8081/WcfServiceHost/Service1.svc"
                binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IService1"
                contract="ServiceReference1.IService1" name="NetTcpBinding_IService1">
                <identity>
                    <dns value="DV9500T" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

Add Username and Password Input To Client Form

Now that all the hooks are in place for the username/password authentication, we need to update the client UI to allow the user to enter a username and password.

Finally, add the code that will pass the client credentials to the service.

proxy.ClientCredentials.UserName.UserName = this.txtUsername.Text;
proxy.ClientCredentials.UserName.Password = this.txtPassword.Text;

Here’s what the code looks like on the client:

        private void btnGetData_Click(object sender, EventArgs e)
        {
            //Since more than one endpoint configuration exists in the app.config,
            //specify the configurationBinding that we want to use.
            using (ServiceReference1.Service1Client proxy =
                new ServiceReference1.Service1Client("NetTcpBinding_IService1"))
            {
                //Send credentials
                proxy.ClientCredentials.UserName.UserName = this.txtUsername.Text;
                proxy.ClientCredentials.UserName.Password = this.txtPassword.Text;

                string response = proxy.GetData(Convert.ToInt32(this.nudGetDataInput.Value));
                this.txtGetDataResponse.Text = response;
            }
        }

        private void btnGetDataContract_Click(object sender, EventArgs e)
        {
            using (ServiceReference1.Service1Client proxy =
                new ServiceReference1.Service1Client("NetTcpBinding_IService1"))
            {
                //Send credentials
                proxy.ClientCredentials.UserName.UserName = this.txtUsername.Text;
                proxy.ClientCredentials.UserName.Password = this.txtPassword.Text;

                //Create a CompositeType object, then pass it to the service
                ServiceReference1.CompositeType ct = new ServiceReference1.CompositeType();
                ct.BoolValue = this.radGetDataContractTrueInput.Checked ? true : false;
                ct.StringValue = this.txtGetDataContractInput.Text;
                ServiceReference1.CompositeType ctResponse = proxy.GetDataUsingDataContract(ct);

                //Fill the form with the values of the CompositeType
                //object that was returned from the service
                this.txtGetDataContractResponse.Text = ctResponse.StringValue;
                this.radGetDataContractTrueOutput.Checked = ctResponse.BoolValue;
                this.radGetDataContractFalseOutput.Checked =
                    !this.radGetDataContractTrueOutput.Checked;
            }
        }

Note that in our example, the Username authentication is based on the following two conditions:

  1. Username must be greater than three characters
  2. Username must contains the word “wcf”

and the Password authentication is based on the following two conditions:

  1. Password must be greater than six characters
  2. Username must contains the word “pass”

When experimenting with this WCF client, you will notice that when you use an invalid Username and/or Password an exception is thrown that reads:

An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.

You will also notice that the exception is thrown only when we call an operation of the service – Not when we instantiate the proxy or set the ClientCredentials.

Summary

In this project we created a WCF Service and had that service hosted in IIS 7. The interesting part of this project was NOT what the WCF service did, but rather, what we had to do to force the WCF client to pass a Username and Password so that the WCF service can authenticate the user prior to allowing the client to run any of the services operations.

The service implemented Transport AND Message Security – and therefore, we demonstrated how to create a Self-Signed SSL Certificate to use in our development and debugging of the service.

Also, we demonstrated how to create our own custom Membership Provider and override the ValidateUser() method so that we can implement our own procedure for authenticating the WCF client – i.e. we can authenticate by checking against a database or even another WCF service!

Download the source files here

Advertisements

One thought on “WCF Service Using Username/Password Authentication

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s