Publishing a ACS v2 Federated Identity Web Role

After implementing the Windows Azure Platform Toolkit Introduction to the Access Control Service v2, I thought I would migrate the same ACS v2 project to a Web Role. This post is to share with you the steps I followed and how I resolved the issues I came across.

The reason I wanted to do this migration was to enable me to demonstrate on a public web site  the identification of a user known by a common identity provider. I wanted to keep as much of the original solution the same – processing claims, displaying custom information on authentication and customising the login process

Things of note:

  • ACS v2 currently in a labs portal which is different the standard Windows Azure portal. This means you will probably end up swapping between browsers/sessions and possibly Windows Azure accounts.
  • Its helpful, though not necessary, to have Visual Studio configured to publish to your Windows Azure account
  • Single sign out isn’t possible in ACS 2 currently. So this means you will have to close the browser is sign out

Part 1 : Create your project

Whilst a web site and a web application share much of the same code the former cannot be simplified converted to a web role. To do this you will have to create a new web application and a new cloud project to indicate the web application is to be used in the Windows Azure environment

I was following Windows Azure Web Role ASP.NET Application Federated Authentication Using AppFabric Access Control Service (ACS) v2 – Part 1. This would help me setup a new web role to host my existing IIS web site code. The following three steps can be followed:

    1. Create Windows Azure Web Role ASP.NET application project in Visual Studio 2010
    2. Create and configure ASP.NET web application as relying party in ACS v2 Management Portal
    3. Configure Windows Azure Web Role ASP.NET project for federation with ACS v2

If you have created your project from the WAPTK application you will have to:

  1. Copy files and folders from your IIS folder to your Visual Studio project location (and include the folders and files in your project)
  2. Remove quite a bit of unneeded web config.
  3. Change all your CodeFile (non compiled) resources to CodeBehind statements

Part 2: Project amendments

  1. Adding assemblies such as the security token visualiser control.

  2. Include various folders present in the standard WAPTK

    The WIF doesn't exist in Windows Azure so that will need to be copied.

    If  you don’t copy the WIF you will get the following result:

    Could not load file or assembly ‘Microsoft.IdentityModel Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ or one of its dependencies. The system cannot fine the find specified.

  3. To make this work the easiest path is to add the WIF (Microsoft.IdentityModel) to the web role references. This is only a remote problem – on your local machine the WIF is normally is in your local Global Assembly Cache.  Unlike this article which suggests I should add assemblies including the GACutil.exe  I found that I could simply mark the assembly I wanted, in Visual Studio, as Copy to Local Output

Audience URIs are needed for the

    1: <microsoft.identityModel>
   2:      <service> 
   3:         <audienceUris>
   4:             <add value="https://127.0.0.1:8080/" /> 
 You will also need to replace or add an additional relaying party information to the AppFabric labs portal site. 

Part 3: Certificates

When I first published my new site I found the following message to greet me.

Key not valid for use in specified state

I read up on the subject after chancing on the  DPAPI is not available in Windows Azure, where I then found the very helpful general guide and  WIF FAQ. I found some prescriptive guidance next on the  Enabling ​Federated ​Authenticat​ion for ​ASP.NET ​applications ​in Windows ​Azure.

By default WIF SessionTokens use DPAPI to protect the content of Cookies that it sends to the client, however DPAPI is not available in Windows Azure hence you must use an alternative mechanism. In this case, we rely on RsaEncryptionCookieTransform, which we use for encrypting the cookies with the same certificate we are using for SSL in our website.

Because of this, I moved my project to SSL (by adding the endpoint to the web role)  made the necessary portal changes (uploaded service certificate) and then made changes for the Global.asax file. This tested out with a second error:

Value cannot be null. Parameter name: certificate

This is because I was trying to encrypt my cookie using the server certificate but it is null. There is a good article on troubleshooting this type of problem courtesy of Jim Rogers in his blog entry Troubleshooting Azure issues. He adds more useful exception information by adding to the global.asax file the following simple check:

    1: if (e.ServiceConfiguration.ServiceCertificate == null)
    2:  {
    3:      throw new ApplicationException("No site certificate; is it set up in web.config?");
    4:      // Make sure you've got the service certificate set up in the web.config - in the WIF
    5:      // <serviceCertificate>
    6:      //   <certificateReference x509FindType="FindByThumbprint" findValue="4653AE813BA15DFFB027E3AC147004B2D24F472B" />
    7:      // </serviceCertificate>
    8:  }

The solution to this problem is in the WIF. Its manifestation is in the web.config Identity model. Customisation of the Identity model is the key here:

    1: </federatedAuthentication>
    2: <serviceCertificate>
    3:   <certificateReference x509FindType="FindByThumbprint" findValue="26C1……" />
    4: </serviceCertificate>

If you run this locally you will find that you may receive the following error:

Configuration Error

Error: 'ID1039: The certificate's private key could not be accessed. Ensure the access control list (ACL) on the certificate's private key grants access to the application pool user.

To fix this error I used the advice from Dominick Baier in the Windows Azure forums. He details that the App Pool runs under the Network Service account.  You need to give this account read access to the private key. Using MMC certificates snap-in (certmgr.msc) for that. Navigate to the certificate -> right click -> manage private key.

And when you finally publish your Web Role to Azure you may receive

ID1059: Cannot authenticate the user because the URL scheme is not https and requireSsl is set to true in the configuration, therefore the authentication cookie will not be sent. Change the URL scheme to https or set requireSsl to false on the cookieHandler element in configuration.

This one being rather simple. Either use entirely HTTPS endpoints on your application or switch off requireSSl=”true” in your cookieHandler in the web config.

At this point is time to congratulate yourself on making a config driven custom security applciation

Recap:

If you would like to use a federated login no a  Windows Azure Web Role you will need the following (in  addition to the web role setup):

  1. microsoft.identityModel populated correctly in web.config

    • e<federatedAuthentication> with appopriate service details
    • Audience Uris
    • Service certificate (which previously is uploaded to portal site and in service definition for SSL)
  2. The relay party setup correctly in app fabric portal site

    And if you are using a custom login page you will need

  3. A login page. The login script will be, at least, configured as corresponds to the application integration section of the portal site:

        1: <!-- This script gets the HRD metadata in JSON and calls the call-back function which renders the links --> 
        2:  
        3: <script src="https://anztest.accesscontrol.appfabriclabs.com:443/v2/metadata/IdentityProviders.js?protocol=wsfederation&realm=http%3a%2f%2f127.0.0.1%3a82%2f&reply_to=&context=&request_id=&version=1.0&callback=ShowSigninPage" type="text/javascript"></script>
    

And the login page should be with access for all, while the site should be denying access to the anonymous user by default in the system. web element

    1: <system.web>
    2:   <customErrors mode="Off"/>
    3:   <httpRuntime requestValidationMode="2.0"  />
    4:   <authorization>
    5:     <deny users="?" />
    6:   </authorization>
    1: <location path="LoginPage.html">
    2:   <system.web>
    3:     <authorization>
    4:       <allow users="*" />
    5:     </authorization>
    6:   </system.web> 
    7: </location>

References:

WIF FAQ

Programming  WIF – A surprisingly good read despite the dry subject area

For further research:

Creating your own identity provider for Windows Azure AppFabric Access Control