Silverlight 4.0 - Secure Communication to WCF service using Custom User Name and Password Validator

Posted by: Mahesh Sabnis , on 10/14/2010, in Category Silverlight 2, 3, 4 and 5
Views: 38047
Abstract: Most of the times, when you develop an application that communicates to a service-oriented solution (like WCF services), it becomes necessary that the service must have some mechanism for authenticating the user, who is making a call to the service.
Most of the times, when you develop an application that communicates to a service-oriented solution (like WCF services), it becomes necessary that the service must have some mechanism for authenticating the user, who is making a call to the service. If it is a Silverlight application that is making a call to such a service, then as a developer you should be aware of the technique to pass user name/password to the service and the service must have the capability to verify these credentials passed to it and authenticate the user.
In this article, I have designed a WCF service which has the custom validation built. We will create a Silverlight client application that will then call this service. In the article, the WCF service is hosted in IIS 7.5 with SSL configured. I recommend you read my previous article on Silverlight 4.0 - Calling WCF 4.0 Service over SSL and Self-Signed Certificate
 
Creating WCF service with CustomBinding and CustomValidator
 
Step 1: Open VS2010 and create a blank solution and name it as ‘SILV40_WCF_UserNamePasword_Security’. To this solution, add a new WCF service application project and name it as ‘WCF_CustomSecurity_Service’. In this project, add a reference to ‘System.IdentityModel’ in .NET framework 4.0. Rename ‘IService1.cs’ to ‘IService.cs’ and ‘Service1.svc’ to ‘Service.svc’.
Step 2: In this project, add a new class and name this class as ‘CCustomValidatorClass’. This class is used to validate the credentials passed by the client application to the WCF service. This class is inherited from the ‘UserNamePasswordValidator’ class and provides a method ‘Validate’ which is responsible for performing validation operations. I have hard coded the values to keep things simple, but you can write your own validation logic. The code of the class is as shown below:
C#
using System.IdentityModel.Selectors;
using System.ServiceModel;
 
namespace WCF_CustomSecurity_Service
{
    public class CCustomValidatorClass : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (userName != "mahesh" || password != "mahesh999")
            {
                    throw new FaultException("The provided credentials are invalid.");
            }
        }
    }
}
 
VB.NET (Converted Code)
Imports System.IdentityModel.Selectors
Imports System.ServiceModel
 
Namespace WCF_CustomSecurity_Service
      Public Class CCustomValidatorClass
            Inherits UserNamePasswordValidator
            Public Overrides Sub Validate(ByVal userName As String, ByVal password As String)
                  If userName <> "mahesh" OrElse password <> "mahesh999" Then
                              Throw New FaultException("The provided credentials are invalid.")
                  End If
            End Sub
      End Class
End Namespace
 
Step 3: Open ‘IService.cs’ and write the following ServiceContract, OperationContract and DataContract.
C#
using System.Runtime.Serialization;
using System.ServiceModel;
 
namespace WCF_CustomSecurity_Service
{
    [ServiceContract]
    public interface IService
    {
        [OperationContract]
        Employee[] GetAllEmployees();
    }
 
    [DataContract]
    public class Employee
    {
        [DataMember]
        public int EmpNo { get; set; }
        [DataMember]
        public string EmpName { get; set; }
        [DataMember]
        public int Salary { get; set; }
    }
}
 
 
VB.NET (Converted Code)
Imports System.Runtime.Serialization
Imports System.ServiceModel
 
Namespace WCF_CustomSecurity_Service
      <ServiceContract>
      Public Interface IService
            <OperationContract>
            Function GetAllEmployees() As Employee()
      End Interface
 
      <DataContract>
      Public Class Employee
            <DataMember>
            Public Property EmpNo() As Integer
            <DataMember>
            Public Property EmpName() As String
            <DataMember>
            Public Property Salary() As Integer
      End Class
End Namespace
Step 4: Open Service.Svc.cs and implement the interface as below:
C#
namespace WCF_CustomSecurity_Service
{
    public class Service : IService
    {
        public Employee[] GetAllEmployees()
        {
            return new Employee[]
            {
                 new Employee() {EmpNo=101,EmpName="Mahesh",Salary=78000},
                 new Employee() {EmpNo=102,EmpName="Vikram",Salary=98000},
                 new Employee() {EmpNo=103,EmpName="Suprotim",Salary=178000},
                 new Employee() {EmpNo=104,EmpName="Ravi",Salary=278000}
            };
        }
    }
}
 
 
VB.NET (Converted Code)
Namespace WCF_CustomSecurity_Service
      Public Class Service
            Implements IService
            Public Function GetAllEmployees() As Employee()
                  Return New Employee() { New Employee() With {.EmpNo=101, .EmpName="Mahesh", .Salary=78000}, New Employee() With {.EmpNo=102, .EmpName="Vikram", .Salary=98000}, New Employee() With {.EmpNo=103, .EmpName="Suprotim", .Salary=178000}, New Employee() With {.EmpNo=104, .EmpName="Ravi", .Salary=278000} }
            End Function
      End Class
End Namespace
 
Step 5: Now the important point to note here is the configuration for custom user name password validator in the service behavior. The configuration makes use of CustomBinding and the security mode is set to ‘UserNameOverTransport’. The configuration is as shown below:
<?xmlversion="1.0"?>
<configuration>
 
 <system.web>
    <compilationdebug="true"targetFramework="4.0" />
 </system.web>
 <system.serviceModel>
    <services>
      <servicename="WCF_CustomSecurity_Service.Service"
                behaviorConfiguration="MyServ">
        <endpoint
           address=""
            binding="customBinding"
            bindingConfiguration="CustBinding"
            contract="WCF_CustomSecurity_Service.IService"/>
      </service>
    </services>
    <bindings>
      <customBinding>
        <bindingname="CustBinding">
          <securityauthenticationMode="UserNameOverTransport"></security>
          <binaryMessageEncoding></binaryMessageEncoding>
          <httpsTransport/>
        </binding>
      </customBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behaviorname="MyServ">
          <serviceMetadatahttpsGetEnabled="true" />
          <serviceDebugincludeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <userNameAuthenticationuserNamePasswordValidationMode="Custom"
              customUserNamePasswordValidatorType= "WCF_CustomSecurity_Service.CCustomValidatorClass,WCF_CustomSecurity_Service"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironmentmultipleSiteBindingsEnabled="true" />
 </system.serviceModel>
 <system.webServer>
    <modulesrunAllManagedModulesForAllRequests="true"/>
 </system.webServer>
 
</configuration>
 
 
 
Now go to the WCF service project properties and set the Web server settings as shown below:
mage_3
Create the virtual directory by clicking on ‘Create Virtual Directory’ button. Test the service by browsing it.
 
Creating Silverlight 4.0 client application
 
Step 1 : In the same solution, add a Silverlight application and name it as ‘SILV4_Custom_Security_Client’. In this project, add the service reference of the WCF service created above.
Step 2: Open the MainPage.Xaml and write the following XAML:
<Grid x:Name="LayoutRoot" Background="White">
    <TextBlock Height="52" HorizontalAlignment="Left" Margin="36,11,0,0" Name="textBlock1" Text="Employee Information" VerticalAlignment="Top" Width="350" TextAlignment="Center" FontSize="26" FontWeight="ExtraBold" />
    <sdk:DataGrid AutoGenerateColumns="True" Height="224" HorizontalAlignment="Left" Margin="20,61,0,0" Name="dgEmp" VerticalAlignment="Top" Width="381" />
    <Button Content="Get All Employees" Height="37" HorizontalAlignment="Left" Margin="94,306,0,0" Name="btnGetAllEmp" VerticalAlignment="Top" Width="244" Click="btnGetAllEmp_Click" />
</Grid>
 
image_1
Step 3: On the ‘Get All Employees’ button click event, write the following code. This code passes the user credentials to the WCF service.
C#
private void btnGetAllEmp_Click(object sender, RoutedEventArgs e)
{
    MyRef.ServiceClient proxy = new MyRef.ServiceClient();
    proxy.GetAllEmployeesCompleted += new EventHandler<MyRef.GetAllEmployeesCompletedEventArgs>(Proxy_GetAllEmployeesCompleted);
    proxy.ClientCredentials.UserName.UserName = "mahesh";
    proxy.ClientCredentials.UserName.Password = "mahesh999";
    proxy.GetAllEmployeesAsync();
}
 
void Proxy_GetAllEmployeesCompleted(object sender, MyRef.GetAllEmployeesCompletedEventArgs e)
{
    dgEmp.ItemsSource = e.Result; 
}
 
VB.NET (Converted Code)
Private Sub btnGetAllEmp_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
      Dim proxy As New MyRef.ServiceClient()
      AddHandler proxy.GetAllEmployeesCompleted, AddressOf Proxy_GetAllEmployeesCompleted
      proxy.ClientCredentials.UserName.UserName = "mahesh"
      proxy.ClientCredentials.UserName.Password = "mahesh999"
      proxy.GetAllEmployeesAsync()
End Sub
 
Private Sub Proxy_GetAllEmployeesCompleted(ByVal sender As Object, ByVal e As MyRef.GetAllEmployeesCompletedEventArgs)
      dgEmp.ItemsSource = e.Result
End Sub
Step 4: Run the application and if the credentials are valid, the DataGrid will display all records.
mage_2
The entire source code of this article can be downloaded over here
Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by xcvxczvx on Friday, October 15, 2010 9:30 AM
sdfdsfs
Comment posted by Jeka on Friday, November 26, 2010 5:04 AM
Thanks for amazing article, man.

In asp.net form authentication we have persistence cookie (in this case user can mark 'remember me' chekbox in login page) and next intrance to aplication, user is not required to provide credentials.
How it possible to implement with your sample??

Thanks in advance.  
Comment posted by Jeka on Saturday, November 27, 2010 4:44 PM
Thanks for amazing article, man.

In asp.net form authentication we have persistence cookie (in this case user can mark 'remember me' chekbox in login page) and next intrance to aplication, user is not required to provide credentials.
How it possible to implement with your sample??

Thanks in advance.  
Comment posted by Mahesh Sabnis on Tuesday, November 30, 2010 5:36 AM
Hi Jeka,
  This article makes use of WCF Custom Mechanish for USerName password validation for user to call methods from WCF services and not for providing Log-in functionality. So the mechanism you are expecting will be out of question at this point. If you make use of SqlMembership provider of ASP.NET and link with WCF then it "may be" possible. I will work on it and will let you know.
Thanks
Mahesh Sabnis  
Comment posted by Pandian on Thursday, March 8, 2012 11:37 AM
Thanks for great article.

Please consider the CCustomValidatorClass - Validate method hit the database to check the username and pwd.So when ever a service get call it will check the db for correct user .

What happen if the same user call the same service multiple time? whether each time it will check DB or else it will persist some security token

Please guide me. Thanks in advance
Comment posted by JP on Wednesday, June 5, 2013 8:14 AM
Hi,

I have follow the steps which are described in above post.
When I tried to access the service using localhost
it displayed message like 'Metadata publishing for this service is currently disabled.'
I have added '<serviceMetadata httpGetEnabled="true" />' in web.config but still is displayed same message.
Is there anything missing or any settings like certificate installation is required?

Thanks
JP

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel