Skip to main content

WCF Transport-level Custom Username Authentication won't work in IIS

Update – I have found a way (with a little help from the community!) around the issues discussed in this post – please see this post

I'm working on some WCF stuff at the moment whereby I want to expose business layer functionality to multiple clients (Javascript, Silverlight and WSHttp) and I want to have a security model that can be supported on all of them.  My authentication is totally custom, since we have all of our users in a custom database.

Logically I want to be able to use the WCF security model since it is very rich, and automatic once you've done the configuration.  However - it's a nightmare - because when you expose AJAX - friendly endpoints, you're doing it with WebHttpBinding, and it's security modes are, well, frankly crap; in fact, in almost every way WebHttpBinding should be thought of as a 'special case', which goes against everything that WCF seeks to achieve.

The problem is this - if you have a custom user name and password store, then you have to use either a membership provider (therefore relying on Asp.Net authorisation, and also in my case having to implement or stub out a whole host of features that our user store doesn't itself support), or you can write an entirely custom validator.

Once that's done your options - taken as a whole - are to use user name client credentials on either the transport or message security (using the 'clientCredentialType' attribute available on the <transport> and <message> security elements of a binding).  You also need to wire up the custom username validator - which is done at the service behaviour level:

  1: <serviceBehaviors>

  2:   <behavior name="MyServiceBehavior">

  3:   <serviceCredentials>

  4:     <userNameAuthentication userNamePasswordValidationMode="Custom" 

  5:                             customUserNamePasswordValidatorType="YourType, AssemblyName"/>

  6:   </serviceCredentials>

  7:   </behavior>

  8: </serviceBehaviors>

In a wsHttp environment you can do anything - so I'll leave that one out.


In a SOAP environment, too, it's fine: You'd probably use Message-based security, so that you also have a way clear to enable transport-level security as well.  Not to mention being also able to lock down the transport to further credentials if need be.


When, however, you hit the WebHttpBinding you also hit a massive brick wall.  You can only enable Transport security or TransportCredentialOnly security.  The first is just SSL, the second is no-SSL, but also gives space for the client credentials to be sent, using one of the Http authentication mechanisms, such as Basic, Digest etc (the same ones available on the IIS security tab).


This is where it gets really amusing - custom user name/password combos with Http Transport-based security do not work when hosted in IIS - because IIS steps in and authenticates against the windows user store (AD, local machine etc) automatically.  For a double-kick in the teeth, it's not supported at all when the service is not self-hosted:


For confirmation of this - see this blog article, and this link on MSDN about the UserNamePasswordValidationMode property.


I'm now having to circumvent WCF security and am rolling my own based on an IDispatchMessageInspector implementation, and custom authentication headers.


The next post will be about how Asp.Net Ajax makes it super-easy to add extra headers to requests as they go out of the WebRequestManager class.

Comments

Popular posts from this blog

Asp.Net 2 and 4 default application pool generates CS0016 IIS7.5

Before I start – if you’ve found a bunch of other articles about this around the net, tried the fixes that are mentioned and still not getting any joy – then read on – you might find this solves your problem. Earlier today I discovered that when I run any ASP.Net 2 or 4 application through IIS7.5 using the default application pools (which use ApplicationPoolIdentity) on Windows 2008 R2 x64 I get an error message similar to this: Server Error in '/MvcApplication31' Application. Compilation ErrorDescription: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS0016: Could not write to output file 'c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\mvcapplication31\222b4fe6\4e80a86\App_global.asax.clb4bsnc.dll' -- 'The directory name is invalid. '
Source Error:[No relevant source lines]
Sou…

Serializing to attributes in WCF with DataContractSerializer

It’s a common problem – you want to return an object from a WCF service as XML, but you either want, or need, to deliver some or all of the property values as XML Attributes instead of XML Elements; but you can’t because the DataContractSerializer doesn’t support attributes (you’re most likely to have seen this StackOverflow QA if you’ve done a web search).  Most likely you’ve then migrated all your WCF service code to using the XmlSerializer (with all the XmlElement/XmlAttribute/XmlType attributes et al) – and you’ve cursed loudly.Well, I’m here to rescue you, because it is possible – and the answer to the problem is actually inferred from the MSDN article entitled ‘Types supported by the Data Contract Serializer’.The example I’m going to give is purely for illustration purposes only.  I don’t have a lot of time, so work with me!Create a new Asp.Net WCF service application, you can use Cassini as your web server (probably easier – otherwise you might have to enable Asp.Net compatibil…

Adding ‘Deny’ functionality to AuthorizeAttribute in Asp.Net Web API

For the web service project I’m working on at the moment I need to be able to treat authorization differently based on the hostname of the URL that requests are made through.To state more clearly – these web services will have a ‘sandbox’ mode in addition to the real mode, and the mode a request will operate under is determined as part of the controller-selection phase early in the Web API request lifecycle.  So, say that my web services will be hosted on services.acme.com; the sandbox will simply be sandbox.services.acme.com.Please note – a discussion of how this is implemented is entirely outside the scope of this article; but I’ll just say that I’ve developed an in-house multi-tenancy layer for both MVC 4 and Web API that allows us to define ‘brands’ and, under those, you can then redefine content, controllers, and even the DI container that is used.These services are going to require caller-level authentication for most operations via SCRAM Authentication (RFC 5802), and as such m…