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.

Sunday 16 January 2011

Building secure, authenticated WCF services for Windows Phone 7 using the ASPNET membership framework


I have done a bit of digging around in order to work out how to "go public" with many of the internal facing services that are offered to our enterprise and applications.

With the exception of 1 or 2 web services that we expose to our partners on a private circuit, we do not have any public web services so our security considerations to-date have been very different.

We use ASPNET membership as the basis for our authorisation and authentication so it makes sense to re-use this for our phone applications. In later posts I will look at how federation can be used to achieve a more seamless login approach.

This particular approach uses the "stateless" model where credentials are passed for every service call. There are other options which I will cover in later posts.


1. Admin/sa access to SQL Server 2005 or 2008

2. A VeriSign SSL certificate or a certificate where the root CA is VeriSign (such as a GoDaddy issued cert for use with SSL)

3. Visual Studio 2010 with the Windows Phone 7 emulator and tools

Anyway, I have broken this into stages on the basis that you may already have some of the below in place or ready - that was my situation, if so jump to Step 3.


Step 1: Creating the ASPNET membership database

Goal: Create the database, roles and associated stored procedures to support the ASPNET membership framework

I'm using a local instance of SQL Server 2008 R2 but this will work equally well on 2005 or SQLExpress. Launch SQL Server management studio and create a new database, let's call it ASPNET

Run the following command from the Framework libraries. This needn't be in the V4 library as I have been using this since .NET V2.0
Running this without any command line parameters will launch the wizard which keeps things simple



Confirm your settings in the wizard and all of the default tables for the ASPNET membership, roles, personalisation, applications etc. with associated stored procedures have been created for you.



Step 2: Tools to manage the ASPNET membership data

Goal: Demonstrate how to configure users and roles within the ASPNET tables

In my case at least, we have developed a custom tool for managing our authentication and authorisation data, but Microsoft (as part of the ASP.NET experience in Visual Studio) have created a nice tool to get you going.

In Visual Studio 2010 (this is also available in 2008) create a new ASP.NET web application. The standard template will create all of the membership tables and stored procedures for you in a local .mdf as well as a simple project with a login control that makes use of the membership framework. We will make use of this standard template later for testing our configuration.

To see the admin UI, simply select "Project--> ASP.NET Configuration" from within VS and this will launch the following admin site - for free!


This points to a local SQLExpress .mdf so  we need to change the configuration slightly to point at our SQL 2008 database.

In the web.config for the site, change the following entries in line with the database that contains your membership schema, for this example it is my local machine "." with a database of ASPNET

    < add name ="ApplicationServices "
        connectionString=" data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnetdb.mdf;User Instance=true"
        providerName=" System.Data.SqlClient" />
    < add name ="ApplicationServices "
        connectionString=" data source=.;Integrated Security=SSPI;Initial Catalog=ASPNET"
        providerName=" System.Data.SqlClient" />

It is worth reviewing the other entries in the web.config to familiarise yourself with the membership and role provider entries.

If you re-launch the admin tool you can now set up your users and roles. 


Step 3 - Create the Users and Roles used in this example

Goal: Create some standard users and roles for this sample

For the purpose of this example, I am going to create the following users

     jon who is an administrator role

     sarah who has a user role

In the Web Site Administration Tool click on the Security tab

Enable Roles and then create the following roles

     admin, user

Go back to the security home and create two users

jon (with an admin and user role)

sarah (with a user role)

the password used will be Complex!

To test this set up, add the following code to the page load event of the web project. I have kept this simple as we are not really too interested in authentication and authorisation for standard web sites, but it is a good place to start.

        protected void Page_Load(object sender, EventArgs e)
           if (User.Identity.IsAuthenticated)
               if (User.IsInRole("admin" ))
                    Response.Write( "You have admin rights\n");

               if (User.IsInRole("user" ))
                    Response.Write( "You have standard user rights\n" );

                Response.Write( "You need to log in");

By logging in and out of the site using the different usernames you can now see how content can be controlled using authentication (identifying the user) and authorisation (identifying the roles associated with the user).



Step 4: Creating a Silverlight Enabled WCF Service

Goal: Demonstrate how Silverlight and WCF work together

Silverlight and moreover  WinPhone7 has a restricted set of WCF capabilities. In order to authenticate the user for a WCF call we have to send the username/password in clear text in the SOAP header. DON'T PANIC. By using https and some simple config all of the heavy lifting is done for us.

1. Add a WCF Service Application project named SimpleService

2. Delete the default Service.svc and IService.cs

3. Add a new item from the Silverlight item templates: Silverlight-enabled WCF Service. Call this Simple.cs

4. Change the Simple.svc code to reflect the following code

namespace SimpleService
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode .Allowed)]
   public class Simple
        [ OperationContract]
       public bool DoWork()
           return true ;

It is worth reviewing and digesting the generated WCF config before it is modified...

        < binding name ="SimpleService.Simple.customBinding0">
          < binaryMessageEncoding />
          < httpTransport />
        </ binding>

A custom binding has been created in order to make Silverlight apps faster, essentially this is standard http with binaryMessageEncoding. The binary encoding will typically make the XML on the wire 30-40%  smaller.

At this stage I like to create a unit test. The unit test will simple add a reference to this service to ensure that all is well.


Step 5: Re-configure the service to use https

Goal: Set up https on IIS and WCF

This is not complicated but requires a bit of care.

1. Create a new application on your local IIS instance call SimpleService 


2. Open up the Server Certificates and use this to install/import your Verisign issued cert. Note: this must be the cert that contains the PRIVATE key 


3. Click on the website instance where this app is created (Default Web Site in my case) and then click on the bindings link. Associated https: on port 443 with the certificate. You may need to add https if it doesn't already exist.

Image(8) Image(9)

4. Now that we have https enabled, update our Simple service to make use of it. Fortunately this is very easy.

     Modify the web.config binding entry on the service to read httpsTransport


          < httpTransport />


          < httpsTransport />

If you have a unit test then update the app.config entry as above and update the address to point at the IIS server (instead of the local project instance). 

You will need to publish the service to the IIS server. 

Once published, now is a good time to try and browse to the service to make sure that it is still working.

The SSL certificate will  be for a public URL, so when browsing to https://localhost/SimpleService/Simple.svc (for example) you will see


To overcome this, add an entry into your hosts file, found in C:\Windows\System32\drivers\etc

Where is the URL that the certificate is registered to.

Browsing to should now take you to your service without complaining

As a checkpoint, use your unit test to call the updated service before continuing.


Step 6: Adding the membership provider to the service

Goal: Use configuration to integrate WCF with the ASPNET membership provider

Rather than walk you through this, the inline comments are probably clearer. Key points to note are the membership provider entry and the corresponding reference to it in service credentials section of the binding.

Update your web.config as follows (essentially replace all of the corresponding sections as below).


    <!-- Connection used to point to the ASPNET membership provider database -->
    < add name ="ApplicationServices "
        connectionString=" data source=.;User Id=membershipuser; Password=membershippwd;Initial Catalog=ASPNET"
        providerName=" System.Data.SqlClient" />

    < compilation debug ="true " targetFramework ="4.0 "  />
     <!-- Configure the Sql Membership Provider-->
    < membership defaultProvider ="AspNetSqlMembershipProvider " userIsOnlineTimeWindow=" 15">
      < providers>
        < clear />
        < add name ="AspNetSqlMembershipProvider " type=" System.Web.Security.SqlMembershipProvider " connectionStringName=" ApplicationServices"
            enablePasswordRetrieval=" false" enablePasswordReset=" true" requiresQuestionAndAnswer=" false" requiresUniqueEmail=" false"
            maxInvalidPasswordAttempts=" 5" minRequiredPasswordLength=" 6" minRequiredNonalphanumericCharacters ="0 " passwordAttemptWindow ="10 "
            applicationName=" /" />
      </ providers>
    </ membership>
    < bindings>
      < customBinding>
        < binding name ="SimpleService.Simple.customBinding0 ">
          < security authenticationMode ="UserNameOverTransport ">
            <!-- Placeholder. Add attribute for such things as including Timestamp and Authentication modes-->
            < secureConversationBootstrap />
          </ security>
          <!-- Optimal way to transmit compressed XML SOAP messages -->
          < binaryMessageEncoding />
          <!-- Configure https-->
          < httpsTransport />
        </ binding>
      </ customBinding>
    </ bindings>
    < services>
      < service name ="SimpleService.Simple ">
        < endpoint address ="" binding=" customBinding" bindingConfiguration=" SimpleService.Simple.customBinding0"
         contract=" SimpleService.Simple" />
        < endpoint address ="mex " binding ="mexHttpBinding " contract=" IMetadataExchange" />
      </ service>
    </ services>
    < behaviors>
      < serviceBehaviors>
        < behavior>
          < serviceMetadata httpGetEnabled ="true "/>
          < serviceDebug includeExceptionDetailInFaults ="true "/>
          <!-- specify how the credentials are verified -->
          < serviceCredentials>
            <!-- Use the aspnet membership provider-->
            < userNameAuthentication userNamePasswordValidationMode ="MembershipProvider "  membershipProviderName=" AspNetSqlMembershipProvider"/>
          </ serviceCredentials>
        </ behavior>
      </ serviceBehaviors>
    </ behaviors>
    < serviceHostingEnvironment aspNetCompatibilityEnabled ="true "
     multipleSiteBindingsEnabled=" true" />


Step 7 - Calling the service.

Update your web reference from the unit test and call the service as follows. Note how credentials are passed in through the proxy. To get the required client config it is probably easier to delete and recreate the web reference. Note how credentials are passed in through the proxy in the calling code.

       [ TestMethod]
       public void TestMethod1()
            ServiceReference1. SimpleClient proxy = new ServiceReference1.SimpleClient ();
            proxy.ClientCredentials.UserName.UserName = "jon";
            proxy.ClientCredentials.UserName.Password = "Complex!";
           bool result = proxy.DoWork();

You can now add the same to your Windows Phone app, using the asynchronous WCF approach of course. I created a simple ThisMobile.Phone application and called the https/aspnet service as follows.

namespace ThisMobile.Phone
   public partial class MainPage : PhoneApplicationPage
       public MainPage()
            Loaded += new RoutedEventHandler (MainPage_Loaded);

       void MainPage_Loaded(object sender, RoutedEventArgs e)

            SimpleProxy. SimpleClient proxy = new SimpleProxy.SimpleClient();
            proxy.ClientCredentials.UserName.UserName = "jon";
            proxy.ClientCredentials.UserName.Password = "Complex!";
            proxy.DoWorkCompleted += new EventHandler<SimpleProxy.DoWorkCompletedEventArgs >(client_DoWorkCompleted);

       void client_DoWorkCompleted(object sender, SimpleProxy.DoWorkCompletedEventArgs e)
            LoggedInStatus.Text = "Logged id, hurray";


Hope this was useful....

Cheers - Jon.



patterns & practices: WCF Security Guidance

Configuring Secure Sockets Layer in IIS 7

WCF Services and ASP.NET (Hosting side by side)

WCF, ASP.NET Membership Provider and Authentication Service

Configuring an ASP.NET application to use Membership

How to: Use the ASP.NET membership provider

Manging shared cookies in WCF

Cannot import wsdl:portType

WCF Binary Bindings in Silverlight 3

Whats new with web services in Silverlight 3 Beta

Consuming web services in Silverlight 3

(Security piece from 30 mins in)

Fiddler and the Windows Phone 7 Emulator

Access control and Identity on WP7