Windows Live Data Code Sample

This web application provides sample code that employs Windows Live Data to:

  • Request permission from a user to access the user's Windows Live Contacts.
  • Use the credentials returned on the response to the permission request to retrieve the list of user's contacts.
  • Display the XML information returned.

C# Code Sample

The sample consists of four code files:

  • Default.aspx: Sample site home page. Dynamically generates a link that sends the HTTP permission request when clicked.
  • Privacy.htm: HTML page describing the Web site privacy policy. Windows Live Data requires the Web site to have a privacy policy page.
  • Process.aspx and Process.aspx.cs: These two files process the response to the permission request, retrieve the list of contacts on behalf of the user, and display the XML information returned.

Implementation Notes

Web applications access Windows Live Data services using standard HTTP and HTTPS requests, and receive the information returned encoded as XML. This section describes common problems encountered when creating applications that use Windows Live Data.

  • Windows Live Data uses a callback url to pass the result of a permission request to your application. You need to name your Web Site using a complete domain name that can be resolved on the Internet. A local name such as https://mymachine will not work; a fully qualified name like https://mymachine.example.com, assuming it is a registered domain, will work.
  • For security purposes, Windows Live Data requires your Web site to have an SSL certificate. You can bypass this requirement for testing purposes by using the NoSSL option on the permission request url, and replacing 'https:' with 'http:' in the returnUrl and privacyUrl variables on Default.aspx.
  • For a more comprehensive explanation of Windows Live Data concepts used by the sample code, see Getting Started with Windows Live Data.

Example

Use the template code below to create the four code files needed to run the application. You can use Visual Studio 2005 or Visual Web Developer 2005 to create a Web Site, or simply copy the code to an IIS application directory.

Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="https://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Windows Live Data Demonstration Site</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h1>
            Windows Live Data Demonstration Site</h1>
        <p>
            This site offers a simplified demonstration of how a site can use Windows Live data.
            We will demonstrate three things:</p>
        <ul>
            <li>Requesting read-only permission to access your Windows Live address book</li>
            <li>Obtaining your Windows Live<i> Owner Handle</i> (i.e. email address) and a
                <i>Domain Authentication Token</i></li>
            <li>Using the Owner Handle and Domain Authentication Token to view your address book</li>
        </ul>
        <p>
            Click on the link below to be taken to the Windows Live data permission page so
            that you can grant this site read-only access to your Windows Live address book.
            If you don't already have a Windows Live address book, you'll have the opportunity
            to create one.
            </p>
        <script language="javascript" type="text/javascript">
            //On a site that knows its URL, this link can be static
            //For demonstration purposes, we supply code that will create the link dynamically
            //
            //Base URL for Windows Live data permissions
            var pguxUrl = "https://ux.cumulus.services.live.com/pgux/default.aspx?";
            //This site
            var host = window.location.hostname;
            if (window.location.port)
                host = host + ":" + window.location.port;
            //This directory
            var path = window.location.pathname;
            var i = path.lastIndexOf("/");
            path = path.substr(0,i);
            //Location of processing page (customize if desired)
            var returnUrl = "https://" + host + path + "/Process.aspx";
            //Location of privacy page (customize if desired)
            var privacyUrl = "https://" + host + path + "/Privacy.htm";
            //Request for read-only permission to Live Contacts
            var permissionString = "LiveContacts.ReadOnly";
            //Construct and emit destination URL in a link
            var targetUrl = pguxUrl + "rl=" + returnUrl + "&pl=" + privacyUrl + "&ps=" + permissionString;
            //Uncomment the next line if you are *not* using SSL (https://) for the returnUrl
            //targetUrl = targetUrl + "&NoSSL";
            document.write("<a href=\"" + targetUrl + "\">Grant Permission</a>");
        </script>
        <p>Note that the "Grant Permission" link above was constructed and emitted dynamically with JavaScript, 
        to allow this code to run on any site. On a real site that knows its URL and the locations 
        of its request processing and privacy policy pages, this link can be static.</p>
        <p>To revoke the permissions given, go to the <a href="https://ux.cumulus.services.live.com/prux/">Revocation Page</a>.</p>
    </div>
    </form>
</body>
</html>
Privacy.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" >
<head>
    <title>Privacy Policy</title>
</head>
<body>
This site will not store your personal information.
</body>
</html>
Process.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Process.aspx.cs" Inherits="Process" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="https://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Windows Live Data Demonstration Site</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h1>
            Windows Live Data Demonstration</h1>
        <p>
            This page processes the permission returned from Windows Live data in two steps
            for demonstration purposes. On a real site, you would most likely combine the steps
            and eliminate the "Get Address Book" button. The following values have been extracted
            from the form passed to this page, at Page Load:</p>
        <p>
            Response Code:
            <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label></p>
        <p>
            Owner Handle:
            <asp:Label ID="Label2" runat="server" Text="Label"></asp:Label></p>
        <p>
            Domain Authentication Token:
            <asp:Label ID="Label3" runat="server" Text="Label"></asp:Label></p>
        <p>
            If the response code received was "RequestApproved", then the button below should be enabled.
            Click it to to issue a GET request to Windows Live Contacts, which should
            retrieve the entire address book belonging to the Owner Handle shown above.</p>
       <p>To revoke the permissions given, go to the <a href="https://ux.cumulus.services.live.com/prux/">Revocation Page</a>.</p>
       <p>
            <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Get Address Book" />
        </p>
        <p>
            Request Status:
            <asp:Label ID="Label4" runat="server" Text="Address book not yet requested"></asp:Label></p>
        <h2>
            Address Book Data</h2>
        <p>
            The Address Book Data below was parsed from the XML returned from the GET request
            to Windows Live Contacts, by walking the XML node list recursively.</p>
        <xml><hellp><Item></Item></hellp></xml>
        <p>
            <asp:Label ID="Label5" runat="server" Text="Address book not yet requested"></asp:Label>&nbsp;</p>
        <p>
            For links to more information and source code, click <a href="ForMoreInformation.htm">
                here</a>.</p>
        <p>
            To restart this demonstration, click <a href="Default.aspx">here</a>.</p>
    
    </div>
    </form>
</body>
</html>
Process.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net;
using System.IO;
using System.Xml;
using System.Text;

public partial class Process : System.Web.UI.Page
{
    string domainAuthToken;
    string ownerHandle;
    string responseCode;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            domainAuthToken = null;
            ownerHandle = null;
            responseCode = null;

            //Go through the posted form and extract the values we care about
            System.Collections.Specialized.NameValueCollection postedValues = Request.Form;
            for (int i = 0; i < postedValues.AllKeys.Length; i++)
            {
                String nextKey = postedValues.AllKeys[i];
                switch (nextKey)
                {
                    case "ResponseCode":
                        responseCode = postedValues[i];
                        break;
                    case "OwnerHandle":
                        ownerHandle = postedValues[i];
                        break;
                    case "DomainAuthenticationToken":
                        domainAuthToken = postedValues[i];
                        break;
                    default:
                        break;
                }
            }
            //Display the values retrieved
            //Note that form control state is preserved across postbacks, so we can use these later
            Label1.Text = responseCode;
            Label2.Text = ownerHandle;
            Label3.Text = domainAuthToken;

            //Enable the next step if the request was approved
            if ("RequestApproved" == responseCode)
            {
                //OK, can get information with ownerHandle and domainAuthToken
                Button1.Enabled = true;
            }
            else
            {
                //error
                Button1.Enabled = false;
            }
        }//!IsPostBack
    }//Page_Load
    
    protected void Button1_Click(object sender, EventArgs e)
    {
        //Get the address book. In a real site, this might be done as part of the Page Load handler.

        //refresh variables from the form controls because there has been a postback from the button click
        ownerHandle = Label2.Text;
        domainAuthToken = Label3.Text;

        //Use the owner handle as part of the request URI
        string uri = "https://cumulus.services.live.com/" + ownerHandle + "/LiveContacts";
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        request.UserAgent = "Microsoft Windows Live data demo"; //customize this for your own application
        request.Method = "GET";
        //Put the domain token into a request header.
        //This is one of three posssible authentication options.
        request.Headers.Add("Authorization", "DomainAuthToken at=\"" + domainAuthToken + "\"");
        //Issue the HTTP GET request to Windows Live Contacts.
        try
        {
            //Make a synchronous request to Windows Live Contacts for demonstration purposes.
            //On a real site, it might be better to make an asynchronous request,
            //  using BeginGetResponse. That requires an AsyncCallback delegate.
            //You might be tempted to try this on the client in JavaScript using XMLHttpRequest. 
            //   Don't. It doesn't currently work, since the client doesn't have the correct domain.
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            //The request succeeded if we got this far, so process the response.
            Label4.Text = "Request succeeded";
            //The response body is XML: read the stream into an XML Document.
            XmlDocument contacts = new XmlDocument();
            contacts.LoadXml(new StreamReader(response.GetResponseStream()).ReadToEnd());
            //Show the contacts in an HTML list for demonstration purposes.
            //On a real site, it might be preferable to bind the returned XML into a control for
            //  navigation and manipulation by the user, for instance using a TreeView with an XMLDataSource.
            StringBuilder sb = new StringBuilder();
            FillTree(contacts.ChildNodes, sb);
            Label5.Text = sb.ToString();
            response.Close();
        }//try
        catch (WebException ex)
        {   //The HTTP request failed, so handle the error, or at least display what happened.
            Label4.Text = "Request failed - " + Environment.NewLine + ex.Message;
        }//catch WebException
        catch (Exception ex)
        {   //The parsing failed, so handle the error, or at least display what happened.
            Label5.Text = "Parsing failed - " + Environment.NewLine + ex.Message;
        }//catch Exception
    }//Button1_Click

    //recursive method to walk an XML node list and generate an HTML list
    private void FillTree(XmlNodeList nodeList, StringBuilder sb)
    {
        sb.Append("<ul>");
        foreach (XmlNode node in nodeList)
        {
            switch (node.NodeType)
            {
                case XmlNodeType.Element:
                    sb.Append("<li>" + node.Name + "</li>");
                    if (node.HasChildNodes)
                    {
                        FillTree(node.ChildNodes, sb);
                    }
                    break;
                case XmlNodeType.Text:
                    sb.Append("<li>" + node.Value + "</li>");
                    break;
                default:
                    break;
            }
        }
        sb.Append("</ul>");
    }//FillTree
}//Process (partial) class