Search
Latest Articles
Richard Costall - Richard Costall was sent a preview copy of Manning's Silverlight 4 in action to review, will it be an 'Action Man' or just 'Out of action!'
Richard Allen - Richie Allen breathes a sigh of relief after all the hard work on Fest10 and has time to reflect on the event. WATCH THE VIDEOS
Skip Navigation Links
Login / Register
Article Quote
"The answer lies in the plugability of WCF. A class called the UserNamePasswordValidator can be overridden to do what you need"
Chris Seary
 Member Quotes
 Latest Articles
Manning: Silverlight 4 in Action Review
Richard Costall was sent a preview copy of Manning's Silverlight 4 in action to review, will it be an 'Action Man' or just 'Out of action!'
Fest10 Review
Richie Allen breathes a sigh of relief after all the hard work on Fest10 and has time to reflect on the event. WATCH THE VIDEOS
Packt's Silverlight 4 Data and Services Cookbook review
Richard puts Packt's Silverlight 4 Data and Services Recipes book through it's paces, but how does it fair against Richard's Challenge
Microsoft Silverlight 4 Business Application Development Review
No sooner has Silverlight 4 been released, than a book drops on Rich's doormat - Microsoft Silverlight 4 Business Application Development by Packt
SharePoint Starter No 2
In his second short article on SharePoint Dave answers the question "What is the difference between WSS 3.0 and MOSS 2007?"
Articles...
Conferences Conferences
Mix10
Mix10
Partner Showcase Partner Showcase
At JetBrains, we have a passion for making people more productive through smart software solutions that help them focus more on what they really want to accomplish, and less on mundane, repetitive "computer busy work".
At JetBrains, we have a passion for making people more productive through smart software solutions that help them focus more on what they really want to accomplish, and less on mundane, repetitive "computer busy work".
Powered by ASP.NET 2.0
NxtGenUG Article
Chris Seary Sunday, July 27, 2008
In an in-depth article, Chris Seary shows us how to implement a security regime for WCF based applications using the pluggibility of WCF.
The Article 

WCF - Alternative Authentication Mechanisms for Web Services

The problem of custom authentication

When you access a web site from your browser, the site may ask you for a username and password. Technically, this is really easy to set up within IIS. You can use Basic authentication, whereby the browser pops up a dialog box asking you for your username and password.

This will authenticate you against Active Directory, where your account is stored.

What if you want to validate those credentials against something other than AD? Well, that's easy. ASP.Net incorporates an infrastructure that allows one to use Forms based authentication. In this case, the authentication is taken away from IIS, and performed directly within the application by a web page with text boxes for username and password.

You can thus use another LDAP provider, a SQL database, an XML file or any other username/password store that you prefer. Just make a call to the provider from the web page.

But what about web services? There’s no web page that you can introduce to the application to grab credentials and pass them to your database. So how can we authenticate against a SQL user store (or any other security store) for SOAP web services?

The solution

The answer lies in the plugability of WCF. A class called the UserNamePasswordValidator can be overridden to do what you need. WS-Security supplies a universal format for sending the credentials, which any client and server can use.

Let’s start by setting up a web service using WCF. Here’s the code for the service:

namespace MyServiceHost
{

    [ServiceContract]
    public interface IMyService
    {
        [OperationContract]
        string GetData(string val);

    }

    [MyServiceBehavior]
    public class MyService : IMyService
    {
        string IMyService.GetData(string val)
        {
            string s = System.ServiceModel.ServiceSecurityContext.Current.PrimaryIdentity.Name;

            return "You sent the text:" + val +
                Environment.NewLine + s;
        }
    }
}

It does little more than return the string value passed. However, it also adds the name of the PrimaryIdentity passed to the web service. The Primary Identity, in our example, is the identity that has been validated by our custom authentication mechanism.

Now we’ll host the service by launching it in a Windows application. Pop the following code into a Windows Form_Load event:

            try
            {
                sh = new System.ServiceModel.ServiceHost(typeof(MyService));
                sh.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }

Here’s the config file for the Windows Forms application:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <system.serviceModel>
  <services>
   <service name ="MyServiceHost.MyService"
   behaviorConfiguration="serviceBehave">
    <endpoint 
     address ="https://localhost:8091/DataService"
     binding ="wsHttpBinding"
     contract="MyServiceHost.IMyService"
     bindingConfiguration ="serviceConfig"
     />
   </service>
  </services>
  <bindings>
   <wsHttpBinding>
    <binding name="serviceConfig">
     <security mode = "TransportWithMessageCredential" >
      <message clientCredentialType="UserName"
       negotiateServiceCredential ="False"
       establishSecurityContext ="False"/>
     </security>
    </binding>
   </wsHttpBinding>
  </bindings>
  <behaviors>
   <serviceBehaviors>
    <behavior name="serviceBehave" >
     <serviceCredentials>
      <serviceCertificate
       x509FindType="FindBySubjectName"
       storeLocation="LocalMachine"
       storeName="My"
       findValue="localhost"
       />
      <userNameAuthentication
       userNamePasswordValidationMode ="Custom"
       customUserNamePasswordValidatorType
        ="MyServiceHost.MyValidator, MyServiceHost"
       />
     </serviceCredentials>
    </behavior>
   </serviceBehaviors>
  </behaviors>
  <diagnostics>

 </system.serviceModel>
</configuration>

Let’s look at the different aspects of the file. Firstly, the address, binding and contract for the service:

   <service name ="MyServiceHost.MyService"
   behaviorConfiguration="serviceBehave">
    <endpoint 
     address ="https://localhost:8091/DataService"
     binding ="wsHttpBinding"
     contract="MyServiceHost.IMyService"
     bindingConfiguration ="serviceConfig"
     />
   </service>

wsHttpBinding is used, as we wish to use WS-Security. The address is https, so we’re using Secure Sockets to protect the data as it passes across the network.

Now let’s look at the configuration for the binding:

    <binding name="serviceConfig">
     <security mode = "TransportWithMessageCredential" >
      <message clientCredentialType="UserName"
       negotiateServiceCredential ="False"
       establishSecurityContext ="False"/>
     </security>
    </binding>

The important bit here is the mode. TransportWithMessageCredential means that the transport (in this case, SSL) will provide confidentiality. The credentials (username and password) will be sent in the XML of the SOAP message.

So now we’ve protected the data, and sent the credentials. How do we validate these credentials against our custom data store? We need to introduce a class derived from the UserNamePasswordValidator class:

namespace MyServiceHost
{
    class MyValidator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            bool valid = false;
            if (userName == "MyUser")
            {
                if (password == "Custard")
                {
                    valid = true;
                }
            }
            if (valid == false)
            {
                throw new SecurityTokenException("Incorrect username or password"); ;
            }
        }
    }
}

As we can see, it’s not doing anything sophisticated here when validating. It simply checks that the username is ‘MyUser’, and the password is ‘Custard’. The beauty of this is that we can write whatever code we like to access a SQL database, an XML file, a text file or any other store to validate the credentials.

The userNameAuthentication element in the configuration file plugs in our custom validator.


   <userNameAuthentication
    userNamePasswordValidationMode ="Custom"
    customUserNamePasswordValidatorType
     ="MyServiceHost.MyValidator, MyServiceHost"
    />

I created a Windows client that calls the service. svcutil.exe will create the proxy , and the code simply calls the methods on the proxy:

        private void btnGetData_Click(object sender, EventArgs e)
        {
            try
            {
  //generate an instance of the proxy to access the web service
                MyServiceClient msc = new MyServiceClient();

  //add the credentials to the proxy
                msc.ClientCredentials.UserName.UserName = "MyUser";
                msc.ClientCredentials.UserName.Password = "Custard";

  //set the certificate used for SSL
     msc.ClientCredentials.ServiceCertificate.SetDefaultCertificate(
      System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
                     System.Security.Cryptography.X509Certificates.StoreName.My,
                     System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectName,
                     "localhost");

  //call the service and get the result
                string s = msc.GetData("stuff");

  //show the result of the call in a text box on screen
                txtRes.Text = s;

            }
            catch (Exception ex)
            {
                txtRes.Text = ex.ToString();

            }
        }

When we press the button, we get the following displayed on the Windows Forms application. A browser control was added, to display the XML sent. Note the username and password in the XML:

Conclusion

Previously, with the older web service technology available with .Net 1.x, the only tool for authenticating credentials was Active Directory. Using a different data store involved using custom SOAP headers, which were not standard and would have to be written again for every client and server.

By combining the pluggablity of WCF and the globally accepted WS-Security schema, custom authentication mechanisms can be implemented with a small amount of configuration and one class embodying the call to your security credential store.

About Chris
Chris Seary is an independent security consultant, and is Director of Chris Seary Computing Ltd (www.seary.com). He has worked with many different Microsoft technologies, and for the last few years has specialised in the security aspects of enterprise-level .NET and Java systems.

He regularly contributes to forums, writes articles and white papers, and has a blog devoted (mainly) to IT security (http://blog.searyblog.com/).
Copyright © 2006-2009 NxtGenUG - Powered by ASP.NET 3.5