Using my WCF http Basic Authentication code in VS 2012

I blogged about the implementation of http basic authentication that I made in this post here, but this is a more detailed description of how to get it working in a simple WCF service in Visual Studio 2012.

First, you'll need to set up a basic out-of-the-box WCF service in Visual Studio 2012:

...and change it slightly so that it can be used from a browser, as described in this post here. You should now be able to call your service from any web browser to see it's working properly.

Personally, I just stuck to the default Microsoft code example and added a [WebGet] attribute, like this:

[WebGet(UriTemplate="getdata?num={value}")]
public string GetData(int value)
{
   return string.Format("You entered: {0}", value);
}

...which means that I can call http://localhost:8080/Service1.svc/getdata?num=1 to pass a querystring value to my method.

Now, add these files to your project (BasicAuthenticationAttribute.cs, BasicAuthenticationHttpHeaderInjector.cs and BasicAuthenticationPasswordValidator.cs). When you've done that, just add the [BasicAuthentication] attribute to your service class. That's it! Try running the project again.

You should be asked for a username and password by the browser. Now, the WCF service will only return some data when you have used these details: username='user' and password='password'. You can change the BasicAuthenticationPasswordValidator class to implement whatever means you like to check that the user is authorised.

You now have a code-only implementation of http basic authentication for WCF. If you're using IIS, you don't need to configure anything to make this work, as far as IIS is concerned you are using anonymous access. But it should also work in IIS Express or the Visual Studio Development Server.

NOTE: this code makes no attempt to encrypt the username and password you type into the browser. The details will be sent in clear text as an http header. You should at least consider using https to encrypt the data. The purpose of this code is just to show how http basic authentication works, and how it could be done in code.

WCF HTTP Basic Authentication in Code

So I wanted to build a WCF service with a very simple authentication mechanism, and I thought that it would be good to start with my own implementation of http basic authentication - and then I could go from there, adding my own extras to the protocol if I wanted. But I thought that basic authentication over https would at least be a start.

And even though the username and password are sent in clear text, doing that over an https connection should be safe since it's encrypted before going over the wire.

Finally, I wanted to do the whole lot in code, so that I can deploy it to some hosted space without fiddling around in IIS. But also because I like code :-)

The good thing is that basic authentication is supported by most browsers, so I could expose the WCF service as web methods and test it very easily. I have assumed that since it works with all the major browsers I have implemented the protocol correctly.

The end result was a custom attribute which can be added to a WCF service and does all the work for you. The only other thing that needs to be done is modify the method which decides if a user is valid or not.

Here is the source code (click the image to download):

I'll post an example of how to use it (in Visual Studio 2012) in another blog.

WebContentFormat.Raw in your WCF config file

If you want to specify WebContentFormat.Raw in your WCF service - so that it can process any content-type, but want that to happen from the config file and not programatically, here is one approach.

 

1) Add an overridden WebContentTypeMapper in your code, like this:

 

using System;

using System.ServiceModel.Channels;

 

namespace ebMS3.ServiceModel

{

    public class RawContentTypeMapper : WebContentTypeMapper

    {

        public override WebContentFormat

                GetMessageFormatForContentType(string contentType)

        {

            return WebContentFormat.Raw;

        }

    }

}

 

2) Reference this new WebContentTypeMapper in your config file in a customBinding:

 

<bindings>

  <customBinding>

    <binding name="RawReceiveCapable">

      <!-- Provide the fully qualified name of the WebContentTypeMapper  -->

      <webMessageEncoding webContentTypeMapperType=

          "ebMS3.ServiceModel.RawContentTypeMapper, ebMS3.ServiceModel,

                  Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

      <httpTransport manualAddressing="true" maxReceivedMessageSize="524288000"

            transferMode="Streamed" />

      <!-- maxReceivedMessageSize is 500 Mb -->

    </binding>

  </customBinding>

</bindings>

 

3) Finally, use the new customBinding on your endpoint, also in your config file:

 

<endpoint contract="MyContract" behaviorConfiguration="MyEndpointBehavior"

binding="customBinding" bindingConfiguration="RawReceiveCapable" />

That's it, another job done...

Custom wsdl from WCF

I wanted my WCF service to export specific metadata.  Essentially I am building it contract-first and I only want the users to see the hand-crafted wsdl and xsd files that have been created.  If the web service was running in IIS that would be easy, I'd simply set the location of the metadata explicitlly in the web.config file, like this:

 

<serviceBehaviors>

  <behavior name="MyBehavior">

    <serviceMetadata httpGetEnabled="true"

    externalMetadataLocation="MyCustomWsdl.wsdl" />

    <serviceDebug includeExceptionDetailInFaults="false" />

  </behavior>

</serviceBehaviors>

 

But this time my WCF service is running inside a Windows Service, so I can't just let IIS serve the wsdl as a page, unless I install the wsdl file on some other web server, which makes for a deployment nightmare.  So I set about finding out how to override the mechanism that WCF uses for sending the auto-generated wsdl.  I was thinking about overriding that.  I found that I could easily add a class that implements the IWsdlExportExtension and IEndpointBehavior interfaces, and it did allow me to do some fiddling with the generated wsdl, but not at the level I wanted.  I wanted total control, essentially streaming the files that had been hand crafted so carefully.  For a minute, I even considered implementing an HttpListener to send out the files :-0

 

The solution that I have settled on is to add another WCF service to my Windows Service.  This is a simple WCF service that responds to http GET commands, the interface looks like this:

 

[ServiceContract]

public interface ICustomWsdl

{

   [OperationContract]

   [WebGet()]

   Stream MetaData(string name, string extension);

}

 

By returning a Stream, we are able to avoid sending the xml decoration around the response, which would happen if we returned a string, for example.  The basic implementation of this service looks like this:

 

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]

public class CustomWsdlService : ICustomWsdl

{

   private string renameXsd(string src)

   {

      XmlDocument xdoc = new XmlDocument();

      xdoc.LoadXml(src);

      XmlNodeList xmlnodes = xdoc.GetElementsByTagName("xsd:include");

      updateNodes(xmlnodes);

      xmlnodes = xdoc.GetElementsByTagName("xsd:import");

      updateNodes(xmlnodes);

      return xdoc.InnerXml;

   }

 

   private void updateNodes(XmlNodeList xmlnodes)

   {

      for (int i = 0; i < xmlnodes.Count; i++)

      {

         string xsdName = xmlnodes[i].Attributes["schemaLocation"].Value as string;

         if (!string.IsNullOrEmpty(xsdName))

         {

            string newName = "MetaData?name="

              + Path.GetFileNameWithoutExtension(xsdName) + "&extension=xsd";

            xmlnodes[i].Attributes["schemaLocation"].Value = newName;

         }

      }

   }

 

   public Stream MetaData(string name, string extension)

   {

      string path =

         Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

      string src =

         renameXsd(File.ReadAllText(path + "\\" + name + "." + extension));

      return new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(src));

   }

}

 

To make it work we need to add these settings in the config file:

 

<system.serviceModel>

  <behaviors>

    <endpointBehaviors>

      <behavior name="WebEndpointBehavior">

        <webHttp/>

      </behavior>

    </endpointBehaviors>

  </behaviors>

 

  <bindings>

    <webHttpBinding>

      <binding name="HttpBindingConfig"/>

    </webHttpBinding>

  </bindings>

 

  <services

    <service name="CustomWsdlService">

      <endpoint address="wsdl"

      behaviorConfiguration="WebEndpointBehavior" binding="webHttpBinding"

      contract="Aggregation.Broker.Core.ICustomWsdl"

      bindingConfiguration="HttpBindingConfig">

        <identity>

          <dns value="localhost" />

        </identity>

      </endpoint>

      <host>

        <baseAddresses>

           <add baseAddress="http://localhost:8731/Test/" />

        </baseAddresses>

      </host>

    </service>

  </services>

</system.serviceModel>

 

It will serve up xsd and wsdl files as long as they are in the same folder as the running assembly.  Since it can be called via http GET, you can test it in a browser, like this:

 

http://localhost:8731/Test/wsdl/MetaData?name=MyWsdlFileName&extension=Wsdl

 

...which would serve up a wsdl file named MyWsdlFileName.wsdl as long as it was in the same folder as the running binaries.  Any import or include statements in the metadata will be automatically remapped to the same serving method, so the wsdl can reference seperate xsd files (indeed, xsd files can also reference other xsd files).  All this means that another WCF service can use this in the config:

 

<serviceMetadata httpGetEnabled="true" externalMetadataLocation="URL_AS_ABOVE" />

 

Job done, I now have total control of my metadata, I just place the wsdl and xsd files in with the binaries of my Windows Service.  Finally, I just need to host this new WCF service in the existing Windows Service, which is easy.  Hopefully, that is not a bad solution.  I've tested it by pointing SoapUI at some metadata presented this way, and it seems fine, happy days.

Alternative WCF ServiceHost

I'm writing a test harness that will try out different WCF services and allow us to fire test data at them.  But I also want to be able to override some of the behaviour of the WCF services during the tests.  So I've built some code to look inside the assemblies in a particular folder and determine if they contain any classes that implement the WCF service interfaces.  Then I can create instances of those classes at runtime and host them as WCF services inside my test harness.  This means that I can switch between various new WCF classes from the UI of the test harness.  However, when swapping the service at runtime, I was getting this error: System.InvalidOperationException: Service 'TestHarness.ATestService' has zero application (non-infrastructure) endpoints.  That's because I was now using a differently named WCF service than the one named in the config file.  So, this was my solution:

 

public class NamedServiceHost : ServiceHost

{

    public static string configName { get; set; }

 

    public NamedServiceHost(object singletonInstance, params Uri[] baseAddresses)

        : base(singletonInstance, baseAddresses)

    { }

 

    protected override void ApplyConfiguration()

    {

        if (!string.IsNullOrEmpty(configName))

        {

            Description.ConfigurationName = configName;

        }

        base.ApplyConfiguration();

    }

}

 

I can use it instead of the normal ServiceHost class, like this:

 

NamedServiceHost.configName = "MyNamespace.MyService";

ServiceHost = new NamedServiceHost(singletonInstance);

ServiceHost.Open();

 

Now I can tell the service host what name I'd like to use from the config file, rather than letting it pick based on the name of the WCF class.  Unfortunately the ApplyConfiguration() method is called from the constructor of the base class, so the easiest way to tell the class what configuration name you'd like to use is by setting a static property.