Share via


Set WCF Service Authentication to Use a Custom Username & Password over HTTP


It’s a common requirement where you want to authenticate the requests which the clients will make to your WCF services. And let's say that’s using some data which you have in your user table. In WCF, the default, when a user name and password is used for authentication, is let Windows to validate the username and password using Windows Authentication. But if you want to authenticate users with custom validation, of course, that’s possible with WCF, because of this custom validation scheme which is known as Validators. You will just have to create a class which will inherit from UserNamePasswordValidator class (which is provided with the .NET framework itself).

Now let’s see this in action. I have created a WCF application and I will just keep the default classes as is. Now I am going to add a new class which is ServiceAuthenticator and going to inherit from UserNamePasswordValidator. For that, I need to add reference to System.IdentityModel.

ServiceAuthenticator.cs

using System;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens; 
namespace WcfService1
{
    public class ServiceAuthenticator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)

        {

            if (null == userName || null == password)
            {
                throw new ArgumentNullException();
            }
            if (!(userName == "username" && password == "password"))
            {
                throw new SecurityTokenException("Unknown Username or Password");

            }

        }
    }
}

Now I need to configure a binding which supports message security over any transport or transport-level security over HTTP(S). Since I assume all my services are accessed via HTTP, the options I can use are wsHttpBinding or CustomBinding. So I am moving forward with wsHttpBinding and modifying the web.config file to configure custom wsHttpBinding as follows.

<bindings>

  <wsHttpBinding>
    <binding name="MyBinding">
      <security mode="Message">
        <message clientCredentialType="UserName"/>
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

When that's done I am configuring a behavior which specifies that a custom user name and password validator is used for authentication. And now comes another important thing to the picture which is a Certificate. In Message security, the message is encrypted by a service certificate which we are going to configure in web.config. So for that let’s create a certificate first. There is handy tool which you can download from my SkyDrive, which is known as Pluralsight self-cert Tool. This is provided by Pluralsight to create and install certificates.

So let’s see how we can use this great tool to create a new certificate and install it.

http://lh5.ggpht.com/-B4ZrEXIo7kc/Ud2eHf3OWyI/AAAAAAAABoQ/PeRjtZ9-1zg/image_thumb2.png?imgmax=800
Pluralsight self-cert

Make sure to run this tool As Administrator and I have given a name which is “MyCertficate”. I am saving the certificate inside LocalMachine and the store name is “My”. Once you clicked the save button it will show the following message.

http://lh4.ggpht.com/-c3UNzoguVXc/Ud2eIpis2wI/AAAAAAAABog/GUfzv4PEeoA/image_thumb5.png?imgmax=800
Certificate is created and stored.

Now let’s make sure the certificate is successfully created and installed. Go to Run and type mmc.

http://lh3.ggpht.com/-10J7KCmVrfc/Ud2eKGaJ-XI/AAAAAAAABow/pmo3gN92Y6s/image_thumb17.png?imgmax=800
MMC

File Add/Remove Snap-in.

http://lh5.ggpht.com/-mlcwYXppqN4/Ud2eLXF61pI/AAAAAAAABpA/Iz51kHv94Yc/image_thumb18.png?imgmax=800
Add/Remove Snap-in

Click on Certificates and Add.

http://lh5.ggpht.com/-OiYskj7K3BU/Ud2eOq5NtvI/AAAAAAAABpQ/IGwE2Jmy964/image_thumb19.png?imgmax=800
Add Certificate

Computer Account.

http://lh5.ggpht.com/-q-8teQodOD4/Ud2eP4z4JfI/AAAAAAAABpg/1pqukXQVRtE/image_thumb20.png?imgmax=800
Computer Account

Local Computer and Finish.

http://lh4.ggpht.com/-YRHOipsZPXc/Ud2eRAzK18I/AAAAAAAABpw/DC2UgMbTVe8/image_thumb21.png?imgmax=800
Local Computer

Now in mmc, navigate to Certificates->Personal->Certificates. There you can see the certificate I have just created using self-cert.

http://lh4.ggpht.com/-gYRq29PJZ-w/Ud2eSwIX39I/AAAAAAAABqA/zhdIZmSBvak/image_thumb22.png?imgmax=800
View Certificate

Now let’s move back to web.config file and start creating a behavior configuration with the service certificate.

<behaviors>

   <serviceBehaviors>
     <behavior name="MyBehavior">
       <serviceMetadata httpGetEnabled="true" />
       <serviceDebug includeExceptionDetailInFaults="true" />
       
       <serviceCredentials>
         <userNameAuthentication userNamePasswordValidationMode="Custom"
          customUserNamePasswordValidatorType="WcfService1.ServiceAuthenticator, WcfService1" /> 
         <serviceCertificate
           findValue="MyCertificate"
           x509FindType="FindBySubjectName"
           storeLocation="LocalMachine"
           storeName="My" />         
       </serviceCredentials>
     </behavior>
   </serviceBehaviors>
 </behaviors>

As you can see I have mentioned the userNamePasswordValidationMode as “Custom” and mentioned the ServiceCertificate.

Now let’s create the endpoints.

<services>

  <service behaviorConfiguration="MyBehavior" name="WcfService1.Service1">
    <endpoint address="Service1.svc" binding="wsHttpBinding"
              bindingConfiguration="MyBinding"
              contract="WcfService1.IService1" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:34435" />
      </baseAddresses>
    </host>
  </service>
</services>

Please note that I have mentioned the created custom configurations in the service behavior configuration and endpoint binding configuration.

Now that’s almost done. Please make sure you can view your service in the browser. If you are hosting your service application in IIS, an error will be thrown “Keyset does not exist”.

http://lh5.ggpht.com/-2-2nh98hCaI/Ud2eUKXP16I/AAAAAAAABqQ/EyK-tg1HcoQ/image_thumb15.png?imgmax=800
Keyset does not exist.

Download winhttpcertcfg.exe from here. After installing the tool, run the following command on the command prompt as Administrator. Go to directory where you have installed this tool. Default is, ”C:\Program Files (x86)\Windows Resource Kits\Tools”. From inside that directory run the following command.

If your service applications’ application pool is DefaultAppPool:

winhttpcertcfg -g -c LOCAL_MACHINE\My -s MyCertificate -a DefaultAppPool

If it’s another application pool you have created in which the identity is let's say “NetworkService”, then the command should be following:

winhttpcertcfg -g -c LOCAL_MACHINE\My -s MyCertificate -a networkservice

Once you run the command, you will see a message like this.

http://lh4.ggpht.com/-_PgsSS4DgHM/Ud2eVQP9tUI/AAAAAAAABqg/B3eCTip8bDE/image_thumb23.png?imgmax=800
command

Now make sure you can view your service in the browser.

Then let’s move to the final part which is the client app. I have created a console application and added a service reference. And following is my code.

using System;

using ConsoleApplication1.ServiceReference1;
using System.ServiceModel.Security; 
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Service1Client c = new Service1Client();
                c.ClientCredentials.UserName.UserName = "username";
                c.ClientCredentials.UserName.Password = "password";
                c.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
                Console.WriteLine(c.GetData(5));
            }
            catch (MessageSecurityException ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.ReadLine();

        }

    }
}

As long I  can pass a valid username and a valid password to my ServiceAuthenticator class, I can consume the service. If not I will be thrown out with an error.

http://1.bp.blogspot.com/-fRFojUluoC0/Ud2kKWFj6wI/AAAAAAAABqw/LBZLwEyBYjA/s400/Success.png
Success
http://4.bp.blogspot.com/-ysNb-vRKnjs/Ud2kQNDsZBI/AAAAAAAABq4/dw53YTDAdac/s400/Error.png
Error

the Full sample in MSDN Code Gallery.
   Download Sample
   SelfCert

Happy Coding.