Monday 31 October 2011

Azure, ACS & MVC3 Caution

I was about to write a Rob Conery style rant about how this quirk of MVC3, Azure and ACS has been annoying for the last week, but I'll keep it brief and to the point.
I have, what I believe, is a relatively common situation.

I'm building a site using MVC3 and Razor to be hosted on Azure.
I'm using Azure ACS to give me Google, Live, Yahoo and other Identity Provider options to access my site.

The problem I have is that when I bundle my MVC3/ACS solution to the cloud using the out of the box tooling, I hit a problem, in short when I attempt to access a restricted path on the site, the WIF/ACS logon redirect isn't fired and the user is sent directly to the target page, unauthenticated!

The steps I took were as follows:-

1. Create my MVC3 solution/project using the Internet Application template
2. Add an Azure project to my solution and then add a web role reference to the MVC3 project within the same solution.
3. Configure ACS to accept request from, and issue tokens to the instance running on my localhost
4. Use the Windows Identity Foundation Federation Utility to automatically update my web.config with entries required to use Azure ACS as my Identity Provider.
5. Add the following entry to web.config to force authentication when an attempt is made to access the account/signin page

So far so good.I fired it up locally and it worked a treat.

I then added the configuration on the Azure ACS for my Azure hosted version of the solution, using the public facing URL.
Before deploying to Azure, you need to consider what is installed in the GAC on the target machine - in short, not a lot. To overcome this you need to bundle the MVC3 DLL dependencies into your bin folder - known as "Bin Deploying". This is a little clumsy, so there is a handy feature in MVC3 which packages the dependent binaries required on the Azure host.

To use this feature, right click on your MVC3 project and choose: "Add Deployable Dependencies". In the dialog select "ASP.NET MVC". This puts all of these tenuous dependencies in place. *Do NOT select "ASP.NET Web Pages with Razor syntax" - this is NOT what it seems.

This will create/update a folder named _bin_DeployableAssemblies containing all of the non-GAC'd DLL's that you need. At compile time these assemblies are automatically copied across to you bin folder.

With the bin folder ready and the project deployed to the cloud, the ACS authentication simply doesn't kick in, so why?

I retraced my steps and through trial and error and some educated guesses found that the problem was with the _bin_DeployableAssemblies entries, specifically the WebMatrix.WebData dll: a security plugin that clashes with the Microsoft.Identity namespace

Once I knew what the problem was, it was then easy to find someone else who had blogged about this

Copied from this post...

What’s happening is that when you ~/bin deploy MVC and Razor, the Razor DLLs are auto-registering some pre-App_Start code to run (which I thought was a neat idea, until now). For the curious it’s WebMatrix.WebData.PreApplicationStartCode.SetupFormsAuthentication from WebMatrix.WebData.dll. If this assembly is not ~/bin deployed then this pre-App_Start code doesn’t run. This pre-App_Start code will force Forms authentication to be enabled (with the login URL at the aforementioned path) unless it finds config data telling it otherwise. Here’s the kicker: the absence of any config data is sufficient to enable this feature. You have to explicitly disable it (as described above). This is quite annoying.

I concur, annoying! Phil Haack has a good post on bin deploying assemblies.

*this is the key problem, to quote Phil Haack "When building an ASP.NET MVC application, you only need to check the first option. Ignore the fact that the second one says "Razor". ASP.NET Web Pages with Razor syntax was the official full name of the product we simply call ASP.NET Web Pages now. Yeah, it’s confusing.