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 –
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!!!...