Click here to Skip to main content
15,860,972 members
Articles / Security

WCF Service with custom username password authentication

Rate me:
Please Sign up or sign in to vote.
4.91/5 (63 votes)
25 Jul 2010CPOL5 min read 450.6K   16K   163   52
This article describes custom username password authentication without the need of certificate installation on the client side.

Tools used

  1. Pluralsight Self-Cert Tool. This tool is provided by Pluralsight to create and install certificates.
  2. WinHttpCertCfg.exe. Windows HTTP Services Certificate Configuration Tool is a command line tool to grant specific users read right access on a certificate's private key file.
  3. Environment: Visual Studio 2010, and IIS7 or above.

Contents

  1. Introduction
  2. Creating the service
  3. Configuring the service
  4. Configuring IIS and publishing the website
  5. Installing the server side certificate
  6. Setting up the client

Introduction

Windows Communication Foundation comes with a rich set of security features such as transport level message and transport with message; each security type has its own advantages and overheads as well. My application has lots of diverse clients used to connect with the service, and they have to be authenticated from the database, so the best possible solution is message level security using custom username - password authentication. After digging in to the net, I found pieces of information, and with some effort, I implemented a concrete solution which I am hoping is helpful for others.

Creating the service

The solution is created using VS2010, and contains three projects: the WCF Service, the website, and the desktop application which is the client application.

solution.jpg

The WCF Service just contains a function GetServertime():

C#
[ServiceContract]
public interface IService1
{
    [OperationContract]
    string GetServertime();
}

[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class Service1 : IService1
{  
    public string GetServertime()
    {
        return DateTime.Now.ToString();
    }   
}

We create a class and name it UserNamePassValidator. We the implement this code in it:

C#
using System;
using System.ServiceModel;

namespace CustomUsernamePasswordAuth.Service
{
    class UserNamePassValidator : 
          System.IdentityModel.Selectors.UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if(  userName==null ||  password==null)
            {
                throw new ArgumentNullException();
            }

            if (!(userName == "fayaz" && password == "soomro") )
            {
                throw new FaultException("Incorrect Username or Password");
            }
        }
    }
}

This class must be derived from System.IdentityModel.Selectors.UserNamePasswordValidator and override the Validate method. And to validate the user, use any data source; in this example, we will use a hard coded value.

Creating the web application

Add a reference to the service in the web application. Add a text file and rename it to UserNamePassService.svc, and add the following line of code:

Aspx
<%@ ServiceHost Language="C#" Debug="true" 
    Service="CustomUsernamePasswordAuth.Service.Service1" %>

Configuring the Web Service

Modify the web.config and add following lines in it.

Add a service behavior and name it Behavior1. Enable the service meta data by adding <serviceMetadata httpGetEnabled="true"/> so that when we add a service reference into the client application, it fetched the information and creates the proxy classes for us. And the essential part is the service certificate. Certificate creation will be covered in a later section, but now, we have to remember the certificate settings. FindValye="MyWebSite" will be the subject for the certificate CN=MyWebSite, and you can change this value to your domain name or project name.

Set the usernamepasswordvalidation mode to custom, and customUsernameapsswordValidator has to be specify the custom validation class and namespace.

C#
<system.serviceModel>        
    <behaviors>
        <serviceBehaviors>
            <behavior name="Behavior1">
                <serviceMetadata httpGetEnabled="true" />
                <serviceDebug includeExceptionDetailInFaults="true" />
                <serviceCredentials> 
                    <serviceCertificate findValue="MyWebSite" 
                          storeLocation="LocalMachine"
                          storeName="My" 
                          x509FindType="FindBySubjectName" />
                    <userNameAuthentication userNamePasswordValidationMode="Custom"
                     customUserNamePasswordValidatorType="CustomUsernamePasswordAuth.
                        Service.UserNamePassValidator, CustomUsernamePasswordAuth.Service" />
                </serviceCredentials>
            </behavior>             
        </serviceBehaviors>
    </behaviors>

Set up the binding configuration as shown below. Name it Binding1 and set the security mode as Message and clientCredentialType as "username".

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

Now we will set up the service endpoint. There are two endpoints: wsHttp endpoint, and Mex end point for metadata exchange. The base address is http://localhost/. The fully qualified service address will be http://localhost/UserNamePassService.svc.

XML
<services>
    <service behaviorConfiguration="Behavior1" 
              name="CustomUsernamePasswordAuth.Service.Service1">
    <endpoint address="" binding="wsHttpBinding"    
              bindingConfiguration="Binding1"
              contract="CustomUsernamePasswordAuth.Service.IService1" />
     <endpoint address="mex" binding="mexHttpBinding" 
        contract="IMetadataExchange" />
            <host>
                <baseAddresses>
                    <add baseAddress="http://localhost/" />
                </baseAddresses>
            </host>
        </service>
    </services>         
</system.serviceModel>

Note: if the website is going to be hosted on a specific port in IIS, as in this example, we have hosted the website in IIS on port 83, http://localhost:83/UserNamePassService.svc, we don't need to change the port in the configuration file and leave the baseAddress as "http://localhost/".

Creating the site in IIS 7

Open IIS Manager. Right click Sites and Add Website. Name it as WebSite, set Application pool to DeafaultAppPool, and select the physical path and set port to 83. As shown below:

iis.jpg

Set the DefaultAppPool Framework version to 4.0.

iis2.jpg

Publish Site to IIS

Right click the website project in Solution Explorer and publish it. Select Publish method as File system, and Target location as http://localhost:83, as shown in the figure below:

publsih.jpg

Browse the site. Open Internet Explorer and type http://localhost:83/UserNamePassService.svc. You will see the error that X.509 could not be found.

browse1.jpg

Installing the certificate

Download the Pluralsight SelfCert from the link given at the beginning of the article. Run the tool as Administrator; otherwise, it will crash.

Configure the settings to install the certificate; refer the screen below.

certificate.jpg

After making the required changes, click the Save button and then you will see the screen below:

Cert1.jpg

After the installation of the certificate, browse the site again, but this time, you should see a different error as shown in the screen below:

browse2.jpg

This error means that the default application pool does not have access rights to the certificate's private key, so now, we have to give read access to the default application pool to do this.

Download WinHttpCertCfg.exe from the link given at the beginning of the article. This tool is a command line tool. After installing the tool, run the following command on the command prompt as Administrator.

C:\Program Files (x86)\Windows Resource Kits\Tools>winhttpcertcfg 
             -g -c LOCAL_MACHINE\My -s MyWebSite -a DefaultAppPool

After running the command, you will see the screen like this:

commandline.jpg

Now browse the site again. And verify the service, it should be up.

browse3.jpg

The final step is to create a client to consume the service

The client application is the desktop application, and just contains the address textbox and the button to get the server time.

client1.jpg

Now add the service reference to the project:

client2.jpg

Add the code to the Button_click event:

C#
private void button1_Click(object sender, EventArgs e)
{
    string time = "";
    // Method 1: Create the client using the configuration file

    Service1Client c = new Service1Client();
    c.ClientCredentials.UserName.UserName = "fayaz";
    c.ClientCredentials.UserName.Password = "soomro";
    c.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = 
                        X509CertificateValidationMode.None;
    time = c.GetServertime();
    MessageBox.Show(time);

    // Method 2: Creating the client by creating endpoint and binding through coding
    var ServiceendPoint = new EndpointAddress(new Uri(txtServiceAddress.Text), 
                          EndpointIdentity.CreateDnsIdentity("MyWebSite"));
    var binding = new WSHttpBinding();
    binding.Security.Mode = SecurityMode.Message;
    binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

    var result = new Service1Client(binding, ServiceendPoint);
    result.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = 
                             X509CertificateValidationMode.None;            
    result.ClientCredentials.UserName.UserName = "fayaz";            
    result.ClientCredentials.UserName.Password = "soomro";
    time = result.GetServertime();
    MessageBox.Show(time);
}

Run the application:

client3.jpg

Running the client from another PC to make sure everything works fine:

client4.jpg

Conclusion

I 'm sure this project will be useful for developers who want to implement custom security. I tried my best to describe each step with a screenshot. I hope you've enjoyed this article. If you like this article, please let me know :). If you have any questions, please feel free to contact me at fayaziiui@gmail.com.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Macmillan Publishing
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionSoap UI Pin
Member 1248354131-Jan-18 4:51
Member 1248354131-Jan-18 4:51 
GeneralMy vote of 5 Pin
Cipherc19-Feb-17 18:46
Cipherc19-Feb-17 18:46 
QuestionNo Account information was found Pin
Member 107304304-Nov-16 22:04
Member 107304304-Nov-16 22:04 
Questionthanks Pin
Member 1175897429-Aug-16 8:19
Member 1175897429-Aug-16 8:19 
QuestionMy vote of 5 Pin
CasaSpider10-Mar-16 21:16
CasaSpider10-Mar-16 21:16 
Praise5 Star Pin
VR Karthikeyan21-Jan-16 23:59
professionalVR Karthikeyan21-Jan-16 23:59 
GeneralMy vote of 5 Pin
Igor Chikalev16-Feb-15 9:36
Igor Chikalev16-Feb-15 9:36 
QuestionMy vote is 5 Pin
Joss Aiyar17-Jul-14 9:16
Joss Aiyar17-Jul-14 9:16 
GeneralBroken Link Pin
Chiramisu14-Jul-14 15:33
Chiramisu14-Jul-14 15:33 
GeneralRe: Broken Link Pin
Member 1275014425-Sep-16 18:01
Member 1275014425-Sep-16 18:01 
GeneralThanks Pin
Akshay Shaha19-May-14 19:12
Akshay Shaha19-May-14 19:12 
QuestionWhy do you add a certificate to the service? Pin
newtmicster4-Apr-14 2:11
newtmicster4-Apr-14 2:11 
QuestionWhere is security Pin
jitendrasahu124231-Mar-14 2:16
jitendrasahu124231-Mar-14 2:16 
SuggestionExcellent article Pin
Gajendra Medatia23-Mar-14 12:02
Gajendra Medatia23-Mar-14 12:02 
GeneralMy vote of 5 Pin
King Coffee21-Aug-13 17:25
King Coffee21-Aug-13 17:25 
QuestionThanks! Pin
Jaime Gonzalez Ulloa2-Jul-13 7:28
Jaime Gonzalez Ulloa2-Jul-13 7:28 
GeneralMy vote of 4 Pin
abhi_here26-Jun-13 5:04
abhi_here26-Jun-13 5:04 
BugI had to use a different command line on windows 7. Pin
abhi_here26-Jun-13 4:42
abhi_here26-Jun-13 4:42 
QuestionTiming Issue Pin
rguez_1815-May-13 19:27
rguez_1815-May-13 19:27 
GeneralMy vote of 5 Pin
Omar Gameel Salem18-Feb-13 3:15
professionalOmar Gameel Salem18-Feb-13 3:15 
QuestionCan't see UserNamePassService Pin
Noe Garcia15-Feb-13 9:44
Noe Garcia15-Feb-13 9:44 
Questioncertificate encodedValue Pin
jason724-Jan-13 3:15
jason724-Jan-13 3:15 
QuestionHow to use same security implementation in HttpWebRequest? Pin
Umais ASGHAR15-Jan-13 22:34
Umais ASGHAR15-Jan-13 22:34 
QuestionImplement WCF Service available both in POX and JSON with custom authentication Pin
Jahan Sarwar5-Dec-12 4:03
Jahan Sarwar5-Dec-12 4:03 
GeneralMy vote of 5 Pin
Keizer61912-Sep-12 20:43
Keizer61912-Sep-12 20:43 
Very Useful, keep it up your good work
- Keizer

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.