Monday, December 26, 2011

Mobile & Global with HTML5, MVC & Windows Azure, Step 5: Secured

In this series of posts we’re progressively demonstrating a mobile and global sample, Responsive Tours. The source code for all 7 steps is on CodePlex at http://responsivetours.codeplex.com.

Here in Step 5 of 7 we’re going to make the site secure by requiring sign-in with a web identity. To be friendly we’ll let users apply an existing web/social identity and give them a choice of several identity providers. In this step we will:

• Configure the Windows Azure Access Control Service with several identity providers
• Implement authentication in the web site with Windows Identity Foundation
• Retrieve claims information (display name) after the user authenticates
Configuring the Access Control Service

The Windows Azure Access Control Service provides claims-based security federation. We’ll be using it to handle authentication. After creating an Access Control Service namespace in the Windows Azure portal it’s necessary to configure it, which will involve several configuration screens.
First off we authorize identity providers. For this sample we’ll use the 3 that are easiest and already built-in to the service: Google, Windows Live ID, and Yahoo!

Next we configure a relying party (RP)—that is, an application permitted to authenticate with this ACS namespace. For the time being, that will be our local machine Window Azure Simulation Environment address, http://127.0.0.1:81/. In later steps we’ll add our deployment addresses in the cloud.

We configure claim rules for our relying party. Claims are name-value pairs our application will be able to retrieve after authentication. Google and Yahoo! will give us a display name, while Windows Live ID does not supply that value.

Finally, the Application Integration area of the portal gives us a WS-Federation Metadata address which we’ll use in the next section.

Implement Authentication with Windows Identity Foundation
We can instrument our application for claims-based authentication with Windows Identity Foundation (WIF). WIF can be used to directly authenticate against security token services (STSs) for domain identity and web identity; and we can also use it to talk to the Windows Azure Access Control Service.

We add WIF to the solution by right-clicking the web project and selecting Add STS reference. That brings up a wizard in which we enter our WS-Federation Metadata address (see previous section). WIF updates the application and its configuration for claims-based security.


The WIF wizard adds the following configuration to Web.config: 
  <microsoft.identityModel>
    <service>
      <audienceUris>
        <add value="http://127.0.0.1:81/" />
      </audienceUris>
      <federatedAuthentication>
        <wsFederation passiveRedirectEnabled="true" issuer="https://MY-ACS-NAMESPACE.accesscontrol.windows.net/v2/wsfederation" realm="http://127.0.0.1:81/" requireHttps="false" />
        <cookieHandler requireSsl="false" />
      </federatedAuthentication>
      <applicationService>
        <claimTypeRequired>
          <!--Following are the claims offered by STS 'https://[MY-ACS-NAMESPACE].accesscontrol.windows.net/'. Add or uncomment claims that you require by your application and then update the federation metadata of this application.-->
          <claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
          <claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
          <!--<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" optional="true" />-->
          <!--<claimType type="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider" optional="true" />-->
        </claimTypeRequired>
      </applicationService>
      <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
        <trustedIssuers>
          <add thumbprint="ED98B07917624AEE89EDDC086589A6149ADE6859" name="https://MY-ACS-NAMESPACE.accesscontrol.windows.net/" />
        </trustedIssuers>
      </issuerNameRegistry>
      <certificateValidation certificateValidationMode="None" />
    </service>
  </microsoft.identityModel>
If we were doing an in-depth application, we’d configure some parts of the site to require authentication and other parts not to. As this is a demo, we’ll just keep things simple and require authentication to access any of it.
Retrieve Claims Information
We’d like to retrieve and show a display name when one is available (some of the identity providers will give us this claim and some do not). Below we add code to the MVC project's Home controller to retrieve a display name claim if available and fashion a welcome string. The welcome string will be passed to the view via its ViewBag.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Data.SqlClient;
using System.Threading;
using Microsoft.IdentityModel;
using Microsoft.IdentityModel.Claims;

namespace html5_mvc_razor.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        [ValidateInput(false)]
        public ActionResult Index()
        {
            LoadClaims();
            LoadPromos();
            return View();
        }

        private void LoadClaims()
        {
            ViewBag.Welcome = "Welcome Back!";
            var principal = Thread.CurrentPrincipal;
            var identity = principal.Identity as IClaimsIdentity;
            var claims = identity.Claims;
            ViewBag.Claims = claims;

            string displayName = null;

            if (claims != null)
            {
                string claimType;
                foreach (Claim claim in claims)
                {
                    claimType = claim.ClaimType;
                    if (claimType.EndsWith("/nameidentifier"))
                    {
                        displayName = claim.Subject.Name;
                        break;
                    }
                }
                if (!String.IsNullOrEmpty(displayName))
                {
                    ViewBag.Welcome = "Welcome back, " + displayName;
                }
            }
        }

        private void LoadPromos()
        {
            ...
        }

    }

    ...
}

<nav>
    @(ViewBag.Welcome)
 <a href="">Home</a>
 <a href="Map">Walking Map</a>
 <a href="#">About Us</a>
</nav>

Testing Authentication
Now it's time to see it work. When we run the solution (with F5 out of Visual Studio), we see a sign-in screen. That's because the application realized it did not have a security token and redirected us to the Access Control Service for sign-in. The sign-in screen requires the user to select an identity provider, after which the user will sign in with that provider.

Once we successfully sign in with an identity provider, we are redirected back to the application which is now happy because it sees a security token this time. We can see the display name claim is being retrieved via the "Welcome back, " message on the web page.

Summary
In Step 5 we secured the site by authenticating web identities using the Windows Azure Access Control Service. Our site now has the following functionality:

• Uses HTML5 and open standards on the web client
• Embodies responsive web design and runs on desktops, tablets, and phones.
• Provides server-side dynamic content (promotional items)
• Provides client-side dynamic content (Bing Maps)
• Is set up for Windows Azure Compute
• Can authenticate against web identities

In the next step, we'll deploy the application to a Windows Azure data center.

No comments: