Saturday, March 8, 2014

Azure WCF Web role over http with wshttpbinding and custom UserNamePasswordValidator – step by step guide


Today I tried to host WCF service using Azure web role with wshttpbinding and custom username and password validator and access it over http.
Wshttpbinding is based on ws- security and it is good option to provide authentication mechanism to WCF service hosted on Azure. Let’s start it right away.

Add Custom username and password validator -
First of all create an Azure cloud service project with one web role and WCF web role. Then add reference to System.IdentityModel to WCF project so that we can use our own custom username and password validator based on UserNamePasswordValidator class. Create a class as shown below in WCF web role project –
/// <summary>
/// Validator class to be used for WCF authentication in case of wsHttpbinding only
/// </summary>
    public class Service1UserNameValidator : UserNamePasswordValidator
    {

        public override void Validate(string userName, string password)
        {
            if (null == userName || null == password)
            {
                throw new ArgumentNullException();
            }

            if (!(userName == "MyUser" && password == "My#Password"))
            {
                // This throws an informative fault to the client.
                throw new FaultException("Unknown Username or Incorrect Password. Unauthorized access to the web service.");
                // When you do not want to throw an infomative fault to the client,
                // throw the following exception.
                // throw new SecurityTokenException("Unknown Username or Incorrect Password");
            }

        }
    }


Configuration to be added in web.config of WCF project –

Now we will configure wshttpbinding for the service based on security mode as Message and clientCredentialType as UserName. Please add below configuration tag in WCF web role
web.config file under System.ServiceModel bindings tag –
<bindings>
      <wsHttpBinding>
        <binding name="wsHttpBinding_IService1">
          <security mode="Message">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings



Now we need to configure the Service Behaviour for WCF Web role.
Under <behaviours> tag add following –
<serviceBehaviors>
        <behavior name="CustomValidator">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <serviceCertificate findValue="localhost" storeLocation="LocalMachine"
              storeName="My" x509FindType="FindBySubjectName" />
            <userNameAuthentication userNamePasswordValidationMode="Custom"
              customUserNamePasswordValidatorType="WCFService.Service1UserNameValidator, WCFService" />
          </serviceCredentials>
        </behavior>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
Under Services tag add following –
<services>
      <service behaviorConfiguration="CustomValidator" name="WCFService.Service1">
        <endpoint address="" binding="wsHttpBinding"
          bindingConfiguration="wsHttpBinding_IService1"
          name="wsHttpEndpoint" contract="WCFService.IService1">
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
You will see that, we have named the behaviour as CustomValidator. Metadata retrieval is allowed over HTTP only and not for https. Then most important part is           <serviceCredentials> tag. This is where we specify the certificate to be used and custom username and password validator.
serviceCertificate tag specifies the certificate to be used. Usually we specify certificates which are issued by certificate authority. As part of this tutorial I am using self signed certificate which gets generated in IIS 7 itself during IIS 7 installation. However please be aware that, this certificate should not be used in production environment.

Open run window and type “inetmgr” without quotes. This will open IIS. Select the computer name in left pane and double click Server Certificates in middle pane to open the feature. You will find that, a certificate named as “IIS Express Development Certificate” is already present. I will use same certificate for this tutorial.




Export/ Import certificate -
During development, this certificate should be present in Trusted People Store of current user and Personal store of Local Machine. During production on cloud service hosting, we need to put the same certificate to some more places which I will talk later.
For now, to add export certificate and import in Trusted People store refer to following link –
The link talks about importing and exporting certificate in Local Machine. Same can be used to import or export certificate in Local user store which can be opened from run window and typing “Certmgr.msc” command.
Add Certificates to Roles -
Next we need to make sure that; self signed certificate is added for WCF web Role and WebRole1 project. Right click WCF role in cloud service project and select Properties. Then click on Certificates option present on left hand side pane. Click on “Add Certificate”; this will add a row. Provide name as localhost, store location as localmachine, store name as my. Now open the certificate from personal store of current user and copy the thumbprint. Add it to the thumbprint section of certificate added in properties of WCF Web Role. Follow the same steps to add localhost certificate for Web Role project.

Next tag is userNameAuthentication. This is where I am specifying my custom class to be used for username and password validation. In this tag WCFService is the name of namespace in which my Service1UsernameValidator class exists.

Adding WCF Web role Service Reference locally in Web Role -
Now we need to start WCF service web role so that service reference can be added in WebRole1 project. Right click WCF Service and debug to start new instance as shown below –



This will start WCF service on some localhost url as shown below - http://localhost:someportnumber/Service1.svc
No we need to add service reference to Web role 1. Righ click WebRole1 project -> click Add service Reference. A window will appear. Click on Discover button. WCF Service Service1 started above will get listed. Keep the reference name as ServiceReference1 only and Click OK to add reference. Open web.config for Webrole1 project. This web.config we will modify to have wshpbinding. You will observe that, in <system.serviceModel>tag basichttpbinding is added.


Copy paste following segment in ServiceModel tag of web.config in WebRole1 project.

<bindings>     
      <wsHttpBinding>
        <binding name="wsHttpBinding_IService1" transactionFlow="false">
          <security mode="Message">
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="ClientCertificateBehavior">
          <clientCredentials>
            <serviceCertificate>
              <!--Setting the certificateValidationMode to PeerOrChainTrust means that if the certificate
              is in the user's Trusted People store, then it will be trusted without performing a
              validation of the certificate's issuer chain. This setting is used here for convenience so that the
              sample can be run without having to have certificates issued by a certificate authority (CA).
              This setting is less secure than the default, ChainTrust. The security implications of this
              setting should be carefully considered before using PeerOrChainTrust in production code.-->
              <authentication certificateValidationMode="PeerOrChainTrust" />
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>

    <client>
      <endpoint address="http://localhost:7493/Service1.svc"
        binding="wsHttpBinding" bindingConfiguration="wsHttpBinding_IService1"
        contract="ServiceReference1.IService1"
        behaviorConfiguration="ClientCertificateBehavior"
        name="wsHttpBinding_IService1" />   
    </client>   
We have specified binding element as wshttpbinding which is similar to that of WCF Service. Next we have specified the endpoint behaviour with certificate validation mode as PeerOrChaintrust. Please refer to the comment mentioned above the tag certificateValidationMode. Hence while moving the deployment to production make sure that you have valid certificate issued by CA. In endpoint just replace the 7493 by your respective port number.
Add sample web form to consume WCF Web role locally -
Now add a web form in the WebRole1 project named as SamplePage.aspx and in page_load event add following code –
namespace WebRole1
{
    public partial class SamplePage : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            WebRole1.ServiceReference1.Service1Client service1 = new ServiceReference1.Service1Client();
            service1.ClientCredentials.UserName.UserName = "MyUser";
            service1.ClientCredentials.UserName.Password = "My#Password";
            string result =  service1.GetData(5);
            Response.Write(result);


        }
    }
}
The username and password specified above are same as the one specified in custom validator in WCF Service web role. Now right click WebRole1 project and select Debug->start new instance. First the custom username validator’s validate method is called making sure that authentication of WCF web role is working fine.
The output should show message as “You Entered: 5.”
This completes the configuration of Azure WCF Web Role using wshttpbinding over http in development environment.
Right now I have hardcoded the username and password in custom validator class. In real scenario, it may be read from database.

Configure port and endpoint for Roles in cloud service -
Now next step is to configure WCF Web role in Azure cloud service using wshttpbinding.
Open ServiceDefinition.csdef file and make sure that the WCF Web role endpoint is configured for http and port 8080 where as Webrole1 is configured for http port 80.
Add certificate to cloud service on Azure Management Portal -
Now I here assume that, you already have a cloud service, storage account created in Azure subscription. Open the cloud service from Azure Management portal and upload certificate localhost.pfx file used in this project. I hope you already have exported the certificate by following the steps mentioned in blog link given above.



I am using localhost certificate here however in real world, using self signed certificate for production deployment is a measure threat. This is only for demo purpose. For production deployment consider a certificate issued by Trusted Authority.

Change WCF End point of WebRole to cloud Service URL -
Now we need to change the endpoint address in WebRole1 project as now it should refer to azure cloud service hosted WCF web role URL. As described above we have marked port 8080 for Azure WCF Web role and my cloud service URL let’s say – kunalwcfdemo.cloudapp.net then my WCF web role URL will be – http://kunalwcfdemo.cloudapp.net:8080/Service1.svc
This is the URL of my endpoint and it should be added in web.config file of WebRole1 project ad comment the localhost URL. So modify the client tag of web.config of Webrole1 project as follows –
<client>
      <!--<endpoint address="http://localhost:7493/Service1.svc"
        binding="wsHttpBinding" bindingConfiguration="wsHttpBinding_IService1"
        contract="ServiceReference1.IService1"
        behaviorConfiguration="ClientCertificateBehavior"
        name="wsHttpBinding_IService1" />-->
      <endpoint address="http://kunalwcfdemo.cloudapp.net:8080/Service1.svc"
        binding="wsHttpBinding" bindingConfiguration="wsHttpBinding_IService1"
        contract="ServiceReference1.IService1"
        behaviorConfiguration="ClientCertificateBehavior"
        name="wsHttpBinding_IService1" >
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
    </client>
Also it might be possible that, the ServiceReference1 might be still pointing to localhost url of WCF web role. Therefore right click

Important:- Adding localhost certificate to Trusted Store of Web Role Instance VM -
Publish the cloud service project. Make sure that you enable remote desktop for the both roles while publish. We will need to make RDP to Web Role machine NOT WCF Role machine and we will need to add localhost certificate in Trusted People store of current user, local machine on Web Role instance virtual machine. So copy localhost.pfx to the desktop of web role instance.
It should be already present in Personal Store of Local Machine. Add it to Trusted People of Local Machine and trusted People of Current User.
Please note that we are not making any change in WCF Web role VM.

Change in Web role VM is required because we are using PeerOrchainTrust and certificate is self signed. If we are using certificate issued by trusted authority and certificate validation mode ChainTrust then this step may not be required. As this tutorial is for demo purpose and we are using self signed certificate we have to add certificate manually by making RDP to web role instance.

Set the SamplePage.aspx as start page for Webrole1 –
If you wish to set any .aspx page as start page of your web role project then please follow the instruction given at this link –

Hushhhhhhhhhhhh….Long process but we are done here…
You should be able to access the WCF web role URL as – 



Note - If the 8080 port in blocked in your organizations network you won’t be able to access it. J
Then you should be able to access Web role URL –



Well just replace the name kunalwcfdemo with your cloud service…I have removed this service from azure subscription to save on cost.
If you need source code of this project, comment your email and I will share it with you.

Hope this whole tutorial helped you. Suggestions are welcome…

Thanks for reading..
Cheers…
Happy WCF…Azuring!!!...

No comments:

Post a Comment