March 2011

Volume 26 Number 03

Forecast: Cloudy - Cloud Services Mashup

By Joseph Fultz | March 2011

Joseph FultzUp until now, I’ve spent time on solutions using Microsoft Azure or SQL Azure to augment solution architecture. This month I’m taking a look at how to combine multiple cloud services into a single app. My example will combine Azure, Azure Access Control, Bing Maps and Facebook to provide an example of composing cloud services.

For those who are a little put off when thinking about federated identity or the real-world value of the social network, I’d like to introduce Marcelus. He’s a friend of mine who owns a residential and commercial cleaning company. Similar to my father in his business and personal dealings, he knows someone to do or get just about anything you want or need, usually in some form of barter. Some might recognize this as the infamous good ol’ boys’ network, but I look at Marcelus and I see a living, breathing example of the Azure Access Control service (or ACS for short) combined with a powerful social network. In real life I can leverage Marcelus and others like him to help me.

However, in the virtual world, when I use a number of cloud services they often need to know who I am before they allow me to access their functionalities. Because I can’t really program Marcelus to serve Web pages, I’m going to use the cloud services in Figure 1 to provide some functionality.

Figure 1 Cloud Services and Their Functionalities

Service Functionality
Azure Host my site and serve pages
Azure Access Control Manage and negotiate authentication between my site and Facebook
Facebook Authenticate users and provide social network services
Bing Maps Visualize friends’ hometowns

The scenario is that navigation to my site’s homepage will be authenticated by Facebook and the claims will be passed back to my site. The site will then pull that user’s friends from Facebook and subsequently fetch information for a selected friend. If the selected friend has a hometown specified, the user may click on the hometown name and the Bing Map will show it.

Configuring Authentication Between Services

The December 2010 issue of MSDN Magazine had a good overview article for ACS, which can be found at msdn.microsoft.com/magazine/gg490345. I’ll cover the specific things I need to do to federate my site with Facebook. To get this going properly, I’m using Azure Labs, which is the developer preview of Azure. Additionally, I’m using Azure SDK 1.3 and I’ve installed Windows Identity Foundation SDK 4.0. To get started, I went to portal.appfabriclabs.com and registered. Once I had access to ACS, I followed the first part of the directions found at the ACS Samples and Documentation page (https://code.msdn.microsoft.com/Windows-Azure-AD-Access-0dcde385) to get the service namespace set up. The next goal was to get Facebook set up as an Identity Provider, but in order to do that I had to first create a Facebook application, which results in a summary like that in Figure 2.

image: Facebook Application Configuration Summary

Figure 2 Facebook Application Configuration Summary

This summary page is important, as I’ll need to use information from it in my configuration of Facebook as an Identity Provider in ACS. In particular, I’ll need the Application ID and the Application secret as can be seen in the configuration information from ACS shown in Figure 3.

image: ACS Facebook Identity Provider Configuration

Figure 3 ACS Facebook Identity Provider Configuration

Note that I’ve added friends_hometown to the Application permissions text box. I’ll need that address to map it, and without specifying it here I wouldn’t get it back by default. If I wanted some other data to be returned about the user by the Graph API calls, I’d need to look it up at the Facebook Developers site (bit.ly/c8UoAA) and include the item in the Application permissions list.

Something worth mentioning when working with ACS: You specify the Relying Parties that will use each Identity Provider. If my site exists at jofultz.cloudapp.net, it will be specified as a relying party on the Identity Provider configuration. This is also true for my localhost. So, in case I don’t want to push to the cloud to test it, I’ll need to configure a localhost relying party and select it, as illustrated in Figure 4.

image: ACS Facebook Identity Provider Configuration: Relying Parties

Figure 4 ACS Facebook Identity Provider Configuration: Relying Parties

Figure 3 and Figure 4 are both found on the same page for configuring the Identity Provider. By the same token, if I only had it configured for localhost, but then attempted to authenticate from my Web site, it wouldn’t work. I can create a custom login page, and there’s guidance and a sample for doing so under Application Integration in the ACS management site. In this sample, I’m just taking the default ACS-hosted page.

So far I’ve configured ACS and my Facebook application to get them talking once invoked. The next step is to configure this Identity Provider for my site as a means of authentication. The easiest way to do this is to install the Windows Identity Foundation SDK 4.0 found at bit.ly/ew6K5z. Once installed, there will be a right-click menu option available to Add STS reference, as illustrated in Figure 5.

image: Add STS Reference Menu Option

Figure 5 Add STS Reference Menu Option

In my sample I used a default ASP.NET site created in Visual Studio by selecting a new Web Role project. Once it’s created, I right-click on the site and go about stepping through the wizard. I’ll configure the site to use an existing Security Token Service (STS) by choosing that option in the wizard and providing a path to the WS-Federation metadata. So, for my access control namespace, the path is:

jofultz.accesscontrol.appfabriclabs.com/
    FederationMetadata/2007-06/
    FederationMetadata.xml

Using this information, the wizard will add the config section <microsoft.identityModel/> to the site configuration. Once this is done, add <httpRuntime requestValidationMode=“2.0” /> underneath the <system.web/> element. Providing that I specified localhost as a relying party, I should be able to run the application, and upon startup be presented with an ACS-hosted login page that will present Facebook—or Windows Live or Google, if so configured. The microsoft.identityModel element is dependent upon existence of the Microsoft.Identity assembly, so you have to be sure to set that DLL reference in the site to Copy Always. If it isn’t, once it’s pushed to Azure it won’t have the DLL and the site will fail to run. Referring to my previous statement about needing to have configuration for localhost and the Azure hosted site, there’s one more bit of configuration once the wizard is complete. Thus, if the wizard was configured with the localhost path, then a path for the Azure site will need to be added to the <audienceUris> element as shown here:

<microsoft.identityModel>
  <service>
    <audienceUris>
      <add value="https://jofultz.cloudapp.net/" />
      <add value="https://localhost:81/" />
    </audienceUris>

Additionally, the realm attribute of the wsFederation element in the config will need to reflect the current desired runtime location. Thus, when deployed to Azure, it looks like this for me:

<federatedAuthentication>
  <wsFederation passiveRedirectEnabled="true" issuer=
   "https://jofultz.accesscontrol.appfabriclabs.com/v2/wsfederation" 
   realm="https://jofultz.cloudapp.net/" requireHttps="false" />
  <cookieHandler requireSsl="false" />
</federatedAuthentication>

However, if I want to debug this and have it work properly at run time on my localhost (for local debugging), I’ll change the realm to represent where the site is hosted locally, such as the following:

<federatedAuthentication>
  <wsFederation passiveRedirectEnabled="true" 
   issuer="https://jofultz.accesscontrol.
   appfabriclabs.com/v2/wsfederation" 
   realm="https://localhost:81/" 
   requireHttps="false" />
  <cookieHandler requireSsl="false" />
</federatedAuthentication>

With everything properly configured, I should be able to run the site, and upon attempting to browse to the default page I’ll be redirected to the ACS-hosted login page, where I can choose Facebook as the Identity Provider. Once I click Facebook, I’m sent to the Facebook login page to be authenticated (see Figure 6).

image: Facebook Login

Figure 6 Facebook Login

Because I haven’t used my application before, Facebook presents me with the Request Permission dialog for my application, as seen in Figure 7.

image: Application Permission Request

Figure 7 Application Permission Request

Not wanting to be left out of the inner circle of those who use such a fantastic app, I quickly click Allow, after which Facebook, ACS and my app exchange information (via browser redirects) and I’m finally redirected to my application. At this point I’ve simply got an empty page, but it does know who I am and I have a “Welcome Joseph Fultz” message at the top right of the page.

Facebook Graph API

For my application, I need to fetch the friends that comprise my social network and then subsequently retrieve information about those friends. Facebook has provided the Graph API to enable developers to do such things. It’s pretty well-documented, and best of all, it’s a flat and simple implementation, making it easy to understand and use. In order to make the requests, I’ll need an Access Token. Fortunately, it was passed back in the claims, and with the help of the Windows Identity Foundation SDK, the claims have been placed into the principal identity. The claims look something like this:

https://schemas.xmlsoap.org/ws/2005/05/  
    identity/claims/nameidentifier
  https://schemas.microsoft.com/ws/2008/06/
    identity/claims/expiration
  https://schemas.xmlsoap.org/ws/2005/05/
    identity/claims/emailaddress
  https://schemas.xmlsoap.org/ws/2005/05/
    identity/claims/name 
  https://www.facebook.com/claims/AccessToken
  https://schemas.microsoft.com/
    accesscontrolservice/2010/07/claims/
    identityprovider

What I really want out of this is the last part of the full name (for example, nameidentifier, expiration and so on) and the related value. So I create the ParseClaims method to tease apart the claims and place them and their values into a hash table for further use, and then call that method in the page load event: 

protected void ParseClaims()
{
  string username = default(string);
  username = Page.User.Identity.Name;
  IClaimsPrincipal Principal = (IClaimsPrincipal) Thread.CurrentPrincipal;
  IClaimsIdentity Identity = (IClaimsIdentity) Principal.Identity;
  foreach (Claim claim in Identity.Claims)
  {
    string[] ParsedClaimType = claim.ClaimType.Split('/');
    string ClaimKey = ParsedClaimType[ParsedClaimType.Length - 1];
    _Claims.Add(ClaimKey, claim.Value);
  }             
}

I create an FBHelper class where I’ll create the methods to access the Facebook information that I desire. To start, I create a method to help make all of the needed requests. I’ll make each request using the WebClient object and parse the response with the JavaScript Serializer:

public static Hashtable MakeFBRequest(string RequestUrl)
{
  Hashtable ResponseValues = default(Hashtable);
  WebClient WC = new WebClient();
  Uri uri = new Uri(String.Format(RequestUrl, fbAccessToken));
           
  string WCResponse = WC.DownloadString(uri);
  JavaScriptSerializer JSS = new JavaScriptSerializer();
  ResponseValues = JSS.Deserialize<Hashtable>(WCResponse);
  return ResponseValues;
}

As seen in this code snippet, each request will need to have the Access Token that was passed back in the claims. With my reusable request method in place, I create a method to fetch my friends and parse them into a hash table containing each of their Facebook IDs and names:

public static Hashtable GetFBFriends(string AccessToken)
{
  Hashtable FinalListOfFriends = new Hashtable();
  Hashtable FriendsResponse = MakeFBRequest(_fbFriendsListQuery, AccessToken);
  object[] friends = (object[])FriendsResponse["data"];
  for (int idx = 0; idx < friends.Length;idx++ )
  {
    Dictionary<string, object> FriendEntry = 
      (Dictionary<string, object>)friends[idx];
    FinalListOfFriends.Add(FriendEntry["id"], FriendEntry["name"]);
  }
  return FinalListOfFriends;
}

The deserialization of the friends list response results in a nested structure of Hashtable->Hashtable->Dictionary. Thus I have to do a little work to pull the information out and then place it into my own hash table. Once it’s in place, I switch to my default.aspx page, add a ListBox, write a little code to grab the friends and bind the result to my new ListBox:

protected void GetFriends()
  {
    _Friends = FBHelper.GetFBFriends((string)_
      Claims["AccessToken"]);
    this.ListBox1.DataSource = _Friends;
    ListBox1.DataTextField = "value";
    ListBox1.DataValueField = "key";
    ListBox1.DataBind();
  }

If I run the application at this point, once I’m authenticated I’ll see a list of all of my Facebook friends. But wait—there’s more! I need to get the available information for any selected friend so that I can use that to show me their hometown on a map. Flipping back to my FBHelper class, I add a simple method that will take the Access Token and the ID of the selected friend:

public static Hashtable GetFBFriendInfo(string AccessToken, string ID)
{
  Hashtable FriendInfo = 
    MakeFBRequest(String.Format(_fbFriendInfoQuery, ID) + 
    "?access_token={0}", AccessToken);
  return FriendInfo;
}

Note that in both of the Facebook helper methods I created, I reference a constant string that contains the needed Graph API query:

public const string _fbFriendsListQuery =   
  "https://graph.facebook.com/me/friends?access_token={0}"; 
public const string _fbFriendInfoQuery = "https://graph.facebook.com/{0}/";

With my final Facebook method in place, I’ll add a GridView to the page and set it up to bind to a hash table, and then—in the code-behind in the SelectedIndexChanged method for the ListBox—I’ll bind it to the Hashtable returned from the GetFBFriendInfo method, as shown in Figure 8.

Figure 8 Adding a GridView

protected void ListBox1_SelectedIndexChanged(object sender, EventArgs e)
{
  Debug.WriteLine(ListBox1.SelectedValue.ToString());
  Hashtable FriendInfo = 
    FBHelper.GetFBFriendInfo((string)_Claims["AccessToken"],  
    ListBox1.SelectedValue.ToString());
  GridView1.DataSource = FriendInfo;
  GridView1.DataBind();
  try
  {
    Dictionary<string, object> HometownDict = 
      (Dictionary<string, object>) FriendInfo["hometown"];
      _Hometown = HometownDict["name"].ToString();
  }
  catch (Exception ex)
  {
    _Hometown = "";//Not Specified";
  }
}

Now that I’ve got my friends and their info coming back from Facebook, I’ll move on to the part of showing their hometown on a map.

There’s No Place Like Home

For those of my friends who have specified their hometown, I want to be able to click on the hometown name and have the map navigate there. The first step is to add the map to the page. This is a pretty simple task and, to that end, Bing provides a nice interactive SDK that will demonstrate the functionality and then allow you to look at and copy the source. It can be found at bingmapsportal.com/ISDK/AjaxV7. To the default.aspx page, I add a div to hold the map, like this:

<div  id="myMap" style="position:relative; width:400px; height:400px;" ></div>

However, to get the map there, I add script reference and a little bit of script to the SiteMaster page:

<script type="text/javascript" src="https://ecn.dev.virtualearth.net/
  mapcontrol/mapcontrol.ashx?v=6.2"></script>      
  <script type="text/javascript">
    var map = null;
    function GetMap() {
      map = new VEMap('myMap');
      map.LoadMap();
    }
  </script>

With that in place, when I pull up the page I’ll be presented with a map on the default position—but I want it to move to my friend’s hometown when I select it. During the SelectedIndexChanged event discussed earlier, I also bound a label in the page to the name and added a client-side click event to have the map find a location based on the value of the label:

onclick="map.Find(null, hometown.innerText, 
    null, null, null, null, true, null, true); 
    map.SetZoomLevel(6);"

In the map.Find call, most of the trailing parameters could be left off if so desired. The reference for the Find method can be found at msdn.microsoft.com/library/bb429645. That’s all that’s needed to show and interact with the map in this simple example. Now I’m ready to run it in all of its glory.

If I’ve configured the identityModel properly to work with my localhost as mentioned earlier, I can press F5 and run it locally in debug. So, I hit F5, see a browser window pop up, and there I’m presented with my login options. I choose Facebook and I’m taken to the login page shown in Figure 6. Once logged in, I’m directed back to my default.aspx page, which now displays my friends and a default map like that in Figure 9.

image: Demo Homepage

Figure 9 Demo Homepage

Next, I’ll browse through my friends and click one. I’ll get the information available to me based on his security settings and the application permissions I requested when I set up the Identity Provider as seen in Figure 2. Next, I’ll click in the hometown name located above the map and the map will move to center on the hometown, as seen in Figure 10.

image: Hometown in Bing Maps

Figure 10 Hometown in Bing Maps

Final Thoughts

I hope I’ve clearly articulated how to bring together several aspects of the Azure, Bing Maps and Facebook—and that I’ve shown how easy it is. Using ACS, I was able to create a sample application from a composite of cloud technology. With a little more work, it’s just as easy to tie in your own identity service to serve as it’s needed. The beauty in this federation of identity is that using Azure enables you to develop against and incorporate services from other vendors and other platforms—versus limiting you into a single choice of provider and that provider’s services, or having to figure out a low-fidelity integration method. There’s power in the Microsoft Azure, and part of that power is how easily it can be mashed together with other cloud services.


Joseph Fultz is an architect at the Microsoft Technology Center in Dallas, where he works with both enterprise customers and ISVs designing and prototyping software solutions to meet business and market demands. He has spoken at events such as Tech·Ed and similar internal training events.

Thanks to the following technical expert for reviewing this article: Steve Linehan