Integrating an Azure application with SharePoint

Posted on 12/27/2011 @ 2:46 AM in #SharePoint by | Feedback | 1498 views

This blogpost is part of a series on “Writing an Azure application and integrating it with SharePoint Securely”.

Table of Contents -

  1. Writing a simple MVC app
  2. Porting our MVC app to Azure – adding table storage.
  3. Adding a worker role
  4. Deploying the application to Azure
  5. Integrating this Azure application with SharePoint <—You are here!


In my previous articles, I demonstrated the basic process of writing an azure app, and deploying it. In the process we enabled RDP, used table storage, queues, (blobs are very similar), and we developed locally and deployed to azure.

Now, lets integrate it with SharePoint. The app we were writing was a ComplaintBox. I’d like to see those complaints inside SharePoint.

Now that you have successfully deployed an azure application that leverages Azure storage, let’s bring this data into SharePoint. There are many possible integration points. You could write a WCF service in the web role and expose it in whichever way you deem fit, and integrate it in SharePoint. Using WIF you could also offer single sign on experience.

In this exercise, we will query Azure table storage via REST API which supports a shared secret security mechanism. For instance, a query such as the below would be successfully authenticated,

  1: GET http://winsmarts.table.core.windows.net/ComplaintEntry() HTTP/1.1
  2: 
  3: DataServiceVersion: 1.0;NetFx
  4: 
  5: MaxDataServiceVersion: 2.0;NetFx
  6: 
  7: x-ms-version: 2009-09-19
  8: 
  9: x-ms-date: Tue, 27 Dec 2011 05:45:15 GMT
 10: 
 11: Authorization: SharedKeyLite winsmarts:<clipped>
 12: Accept: application/atom+xml,application/xml
 13: 
 14: Accept-Charset: UTF-8
 15: 
 16: Host: winsmarts.table.core.windows.net
 17: 
 18: Connection: Keep-Alive
 19: 

Many of the elements here are self explanatory, but there are two things to watch out for,

a) The date has to be in an RFC1123 format.

b) The Authorization we are using is SharedKeyLite. SharedKeyLite is the date concatenated with a newline concatenated with the canonicalURL you are trying to access – this whole concatenated string, done a one-way hash on, using your Azure storage account key. This is achieved in the code shown in the webpart code below,

  1:         protected override void CreateChildControls()
  2:         {
  3:             String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
  4:             string authHeader = CreateAuthorizationHeader(dateInRfc1123Format + "\n/" + AzureStorageAccount + "/ComplaintEntry()");
  5:             Uri uri = new Uri("http://winsmarts.table.core.windows.net/ComplaintEntry()");
  6:             HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
  7:             request.Method = "GET";
  8:             request.Headers.Add("DataServiceVersion", "1.0;NetFx");
  9:             request.Headers.Add("MaxDataServiceVersion", "2.0;NetFx");
 10:             request.Headers.Add("x-ms-version", "2009-09-19");
 11:             request.Headers.Add("x-ms-date", dateInRfc1123Format);
 12:             request.Headers.Add("Authorization", authHeader);
 13:             request.Accept = "application/atom+xml,application/xml";
 14:             request.Headers.Add("Accept-Charset", "UTF-8");
 15: 
 16:             String responseFromServer = "";
 17:             using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
 18:             {
 19:                 Stream dataStream = response.GetResponseStream();
 20:                 using (StreamReader reader = new StreamReader(dataStream))
 21:                 {
 22:                     responseFromServer = reader.ReadToEnd();
 23:                 }
 24:             }
 25: 
 26:             XElement xmlTree = XElement.Parse(responseFromServer);
 27:             var complaints = from complaint in xmlTree.Descendants()
 28:                              where complaint.Name.Namespace == "http://schemas.microsoft.com/ado/2007/08/dataservices"
 29:                              select complaint;
 30: 
 31:             foreach (var complaint in complaints)
 32:             {
 33:                 this.Controls.Add(new Literal() { Text = "<br/><b>" + complaint.Name.LocalName + "</b>:" + complaint.Value });
 34:             }
 35:         }
 36: 
 37:         private string CreateAuthorizationHeader(string canonicalizedstring)
 38:         {
 39:             string signature = string.Empty;
 40:             using (HMACSHA256 hmacSha256 = new HMACSHA256(Convert.FromBase64String(AzureStorageKey)))
 41:             {
 42:                 Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(canonicalizedstring);
 43:                 signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
 44:             }
 45:             string authorizationHeader = string.Format(CultureInfo.InvariantCulture, "{0} {1}:{2}", "SharedKeyLite", AzureStorageAccount, signature);
 46:             return authorizationHeader;
 47:         }

 

Go ahead and run this webpart, it should produce an output shown as below in SharePoint,

Couple of things to note,

  • This won’t work in sandbox solutions and is therefore not office 365 friendly. There are JavaScript libraries available that will give you a HMACSHA256 hash in JavaScript, I recommend not using them – since in Office365/JavaScript based implementation, your storage key will be easily decipherable at the client side.
  • This does not offer user level authentication – it is merely authorization at the table level with a shared key. To enable user level authentication, you will have to author a WCF service and tie it with claims based identities using ACS or a custom STS. More on that later.
  • If you wanted to offer true integration with Office365/JavaScript, where the identity of the individual user mattered in integrating SharePoint with Azure storage, you’d have to use WIF/STS/OAUTH. I’ll cover that in a subsequent blogpost when I’m not so sleepy. For now, enjoy and g’nite!

Sound off but keep it civil:

Older comments..