Developing Windows Phone 7 Applications for SharePoint 2010

The Windows Phone 7 platform contains great out of the box integration with SharePoint under the Office Hub. But what if you want to create your own custom applications on the phone that leverage SharePoint 2010 data and services? In this blog post I will take you through the process of creating your first custom Silverlight application on the phone that consumes SharePoint data.

The Office Hub

The Office hub provides great access to your SharePoint documents. The Office hub also provides a great OneNote experience allowing you to take written and voice notes. These documents and notes are synchronized with the SharePoint server.  PC World has a good article on some of the features.

Windows Phone 7 Spotlights SharePoint Collaboration

2010-02-17: PC World

An application that many users have never seen--Microsoft's SharePoint--plays a key role in Windows Phone 7 Series' Office Hub, the center of business activities in the new smartphone operating system.

Read full article

See it in action: https://www.youtube.com/watch?v=FBMBQNOHzGc

image

The Office hub is not extensible nor can you interact with the hub from another application. The only option is to create a custom Silverlight application. Let's take a look at how to do this.

Custom SharePoint Applications

While the Office hub is interesting and has a lot of capabilities out of the box. As a developer I just want to be able to create my own applications that can leverage the full power of the SharePoint platform. In this sample application I will call the SharePoint Lists.asmx Web Service to retrieve Tasks from the Tasks List. The Tasks are data bound to a ListBox (in green below). There is also a data bound details Grid (in red below) that will show the Task details of the selected item.

image

Forms Based Authentication (FBA)

One of the first hurdles when developing a SharePoint app is the fact that Windows Phone 7 does not support Windows Authentication (NTLM).  This means that you will need to enable FBA on your SharePoint site. In the sample that I created I used a LDAP claims provider. This enabled me to use the same users that I already had in my Active Directory store. I also enabled dual mode so that I could log into the SharePoint site using either Windows Authentication (NTLM) or Forms Based Authentication (FBA). I also enabled anonymous access as well. This allowed me to choose which login method I wanted to use. Without enabling anonymous access the browser would have automatically logged me in with my current NTLM credentials. The bottom line is that you need to enable FBA on your SharePoint sites that you will access from the Phone.

image

On the left side of the above diagram is the “Classic” authentication path. The only option is NTML. If you want to enable FBA then you must choose to create the SharePoint Web Application using “Claims” mode. Once you enable claims mode then you can choose from a number or claim providers.

Getting the Security Token

The process for logging into SharePoint is outlined in the following diagram (and simplified). First the app/user tries to access a secure resource/service. They are redirected to a login page. The login page redirects to the authentication.asmx service. The authentication service returns a security token (FEDAUTH). For every subsequent call the security token is passed to the server. The sever verifies the token and returns the requested resource/service.

image

 

So all you need to do is get the security token and pass it along. It sounds simple enough. The problem is that the FEDAUTH security cookie is marked with a “HTTPOnly” flag, this means that you cannot access this cookie from code. All of the APIs in the stack honor this flag so you can’t get to the FEDAUTH token. Fortunately .NET includes a CookieContainer class that holds the collection of cookies sent and retrieved from a web request.  Here is the “Trick” to make everything work. If you look into this CookieContainer class in the debugger you will not see the FEDAUTH cookie or any other HTTPOnly, but they are there. All you need to do is simply pass the CookieContainer along from call to call. 

Once you have the security token, or at least the CookieContainer that the token is in, you can create your SharePoint application using Web Services or WCF Data Services just as you would with any other SharePoint application. Unfortunately, the Silverlight Client Object Model is not supported on the Phone.

Calling the Authentication.asmx Service

The Authentication.asmx service takes a username and password and returns a FEDAUTH security token as a HTTPOnly cookie. the problem is that you cannot add the service using the Add Service Reference in Visual Studio. The proxy that is generated is not compatible. I would image that you could edit the proxy code to fix the compatibility issues, but I choose to take a different approach. Instead of using Visual Studio to generate a proxy I use the HTTPWebRequest class to make raw calls to the service. Here is the code to call the Authentication.asmx service and fill the CookieContainer with the FEDAUTH security token.

1. First create an event to fire when the authentication is complete. and then create a CookieContainer to hold the security token. I highlighted the key lines.

 public partial class MainPage : PhoneApplicationPage
{
    public event EventHandler OnAuthenticated;

    CookieContainer cookieJar = new CookieContainer(); 

    //Hard coded for Demo
    private string userName = "danj";
    private string userPassword = "pass@word1";

    // Constructor
    public MainPage()
    {
        InitializeComponent();
    }

2. Here is the core code to call the authentication service. You should just copy this code block into your app. The code is in the order that it flows, so first create the request, then fill the body with the SOAP message, then handle the results and fire the OnAuthenticated event. I highlighted the key lines.

         #region SharePoint Authentication
        private void Authenticate()
        {
            System.Uri authServiceUri = 
  new Uri("https://phone.contoso.com/_vti_bin/authentication.asmx");

            HttpWebRequest spAuthReq = 
  HttpWebRequest.Create(authServiceUri) as HttpWebRequest;
            spAuthReq.CookieContainer = cookieJar; 
            spAuthReq.Headers["SOAPAction"] = 
 "https://schemas.microsoft.com/sharepoint/soap/Login";
            spAuthReq.ContentType = "text/xml; charset=utf-8";
            //spAuthReq.Accept = "text/xml";
            spAuthReq.Method = "POST";

            //add the soap message to the request
            spAuthReq.BeginGetRequestStream(
  new AsyncCallback(spAuthReqCallBack), spAuthReq);
        }

        private void spAuthReqCallBack(IAsyncResult asyncResult)
        {
            string envelope =
                    @"<?xml version=""1.0"" encoding=""utf-8""?>
                    <soap:Envelope xmlns:xsi=""https://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""https://www.w3.org/2001/XMLSchema"" xmlns:soap=""https://schemas.xmlsoap.org/soap/envelope/"">
                      <soap:Body>
                        <Login xmlns=""https://schemas.microsoft.com/sharepoint/soap/"">
                          <username>{0}</username>
                          <password>{1}</password>
                        </Login>
                      </soap:Body>
                    </soap:Envelope>";

            UTF8Encoding encoding = new UTF8Encoding();
            HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
            Stream _body = request.EndGetRequestStream(asyncResult);
            envelope = string.Format(envelope, userName, userPassword);
            byte[] formBytes = encoding.GetBytes(envelope);

            _body.Write(formBytes, 0, formBytes.Length);
            _body.Close();

            request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
        }

        private void ResponseCallback(IAsyncResult asyncResult)
        {
            HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
            HttpWebResponse response = 
 (HttpWebResponse)request.EndGetResponse(asyncResult);
            Stream content = response.GetResponseStream();

            if (request != null && response != null)
            {
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    using (StreamReader reader = new StreamReader(content))
                    {
                        //Put debugging code here
                        string _responseString = reader.ReadToEnd();
                        reader.Close();
                    }
                }
            }

            //authentication complete
            OnAuthenticated(null, null);
        }
        #endregion

 

Calling the SharePoint List Service

Once you have the CookieContainer with the FEDAUTH security token you can simply just pass it along and make service calls to SharePoint as you would in any SharePoint app. In this case I used the Add Service Reference tools in Visual Studio to add a reference to the Lists.asmx Web Service.

1. Get the Tasks once the authentication is complete. I highlighted the key lines.

 void MainPage_OnAuthenticated(object sender, EventArgs e)
{
    GetTasks();
}

private void GetTasks()
{
    ListsService.ListsSoapClient lists = new ListsService.ListsSoapClient();
    lists.CookieContainer = cookieJar; 

    lists.GetListItemsCompleted += 
 new EventHandler<ListsService.GetListItemsCompletedEventArgs>
    (lists_GetListItemsCompleted);

    lists.GetListItemsAsync(
        "Tasks",             //List Name
        String.Empty,   //View Name
        null,                   //query
        null,                   //view fields
        null,                   //row limit
        null,                   //query options
        null);                  //webID

}

void lists_GetListItemsCompleted(object sender, ListsService.GetListItemsCompletedEventArgs e)
{
    string result = Convert.ToString(e.Result);

    XElement listItems = e.Result;

    IEnumerable<XElement> rows = 
    e.Result.Descendants(XName.Get("row", "#RowsetSchema"));

    IEnumerable<Task> tasks = from element in rows
                select new Task
                {
                    Title = (string)element.Attribute("ows_LinkTitle"),
                    Status = (string)element.Attribute("ows_Status"),
                    Priority = (string)element.Attribute("ows_Priority")
                };

    listBox1.ItemsSource = tasks;

    //foreach (Task t in tasks)
    //{
    //    string s = t.Title;
    //}

}

public class Task
{
    public string Title { get; set; }
    public string Status { get; set; }
    public string Priority { get; set; }
}

Download the Sample Application

Visit: code.msdn.microsoft.com/SPPhone to download the sample project and learn more about building SharePoint Phone applications.

Next Steps

There is much more to do around developing SharePoint Phone applications. I have some more samples and examples that I will post and blog about in the future. I have also recorded this session last week at the SharePoint Connections Conference in the Hague. So this will be posted as soon as it is ready. I am also working on an MSDN Magazine article that will be be out soon.

So stay tuned as we are just getting started.