Introduction
I've spent lots of time researching and investigating WCF security in Azure, but couldn't find a working solution directly implemented in Azure web app. Lots of stuff about WCF security in IIS, Azure cloud service (webrole/webworker), but nothing about Azure web application (or web api). So here are some simple steps of setting up HTTPS with basic authentication for WCF which worked for me in Azure web app.
How To
We will have to take care of two sides of the wire: the WCF Service itself and its Client
The Service
As you probably already know, HTTPS protocol requires SSL sertificate. The good news is that Azure *.azurewebsites.net domains are already secured by a certificate provided by Microsoft. You can use https://mywebsite.azurewebsites.net to access your site securely. Note however, *.azurewebsites.net is a shared domain, and like all shared domains is not as secure as using a custom domain with your own certificate. Enabling HTTPS for an app in Azure App Service with custom domain is beyond the topic of this article and you could find a usefull info in the following resource https://azure.microsoft.com/en-us/documentation/articles/web-sites-configure-ssl-certificate/
Web.config
Firstly, let's set up web.config of the server:
<system.serviceModel>
<services>
<service name="MyProject.MyService" behaviorConfiguration="serviceBehavior">
<endpoint address="https://mywebapi.azurewebsites.net/MyService.svc" binding="wsHttpBinding"
bindingConfiguration="secureHttpBinding" contract="MyProject.Service.Contracts.IMyService" />
</service>
</services>
<behaviors xdt:Transform="Replace">
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="MyNamespace.ClientAuthenticator, MyNamespace" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="secureHttpBinding"
hostNameComparisonMode="StrongWildcard"
receiveTimeout="00:01:00"
sendTimeout="00:01:00"
openTimeout="00:01:00"
closeTimeout="00:01:00"
maxReceivedMessageSize="2147483647"
maxBufferPoolSize="524288"
messageEncoding="Text"
textEncoding="utf-8"
bypassProxyOnLocal="false"
useDefaultWebProxy="true" >
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
Some elements of interest to highlight here:
- Endpoint is running over HTTPS (
address="https://mywebapi.azurewebsites.net/MyService.svc"
) - WsHttpBinding endpoint binding configuration has
<security>
tag with mode="TransportWithMessageCredential"
and clientCredentialType="UserName"
so that service will expect client credentials in the request. Thus we are enabling WCF Basic Authentication. - ServiceBehavior is set up with ServiceCredentials by means of which we say to WCF what method to run to validate client's credentials. In our case it is
MyNamespace.ClientAuthenticator.cs
class described below.
ClientAuthenticator.cs
Secondly, we'll need an authenticator to validate the client:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IdentityModel.Selectors;
using System.Linq;
using System.ServiceModel;
using System.Web;
namespace MyNamespace
{
public class ClientAuthenticator: UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new FaultException("Credential arguments are not supplied");
}
var appSettings = ConfigurationManager.AppSettings;
if (!string.Equals(userName, appSettings["basicAuthUser"], StringComparison.OrdinalIgnoreCase) && !string.Equals(password, appSettings["basicAuthPsw"], StringComparison.Ordinal))
{
throw new FaultException("Invalid user and/or password");
}
}
}
}
The Client
Web.config
<system.serviceModel>
<client>
<endpoint address="https://mywebapi.azurewebsites.net/MyService.svc" binding="wsHttpBinding"
bindingConfiguration="secureHttpBinding" contract="MyProject.Client.Contracts.IMyService" />
</client>
<bindings>
<wsHttpBinding>
<binding name="secureHttpBinding"
hostNameComparisonMode="StrongWildcard"
receiveTimeout="00:01:00"
sendTimeout="00:01:00"
openTimeout="00:01:00"
closeTimeout="00:01:00"
maxReceivedMessageSize="2147483647"
maxBufferPoolSize="524288"
messageEncoding="Text"
textEncoding="utf-8"
bypassProxyOnLocal="false"
useDefaultWebProxy="true" >
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
Note, there is no way to configure client's credentials in the <system.serviceModel>
tag of web.config file, we'll need to set this up in code.
Supplying client credentials
PeopleServiceClient client = new PeopleServiceClient();
var appSettings = ConfigurationManager.AppSettings;
client.ClientCredentials.UserName.UserName = appSettings["basicAuthUser"];
client.ClientCredentials.UserName.Password = appSettings["basicAuthPsw"];
Conclusion
And that's it, you're good to go, WCF web app in Azure is secured. Remember that the Client has to run over HTTPS as well to communicate with WCF in the right way. Hope this article will save you lots of time in researching this topic. Happy programming.
History
- 16th April, 2016: Initial version.