Jaa


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.

Comments

  • Anonymous
    October 19, 2010
    This code is not working .. Remote server not found error is coming

  • Anonymous
    October 20, 2010
    You will need to setup your own SharePoint server with Forms Based Authentication (FBA) enabled.

  • Anonymous
    October 21, 2010
    I had set up my own sharepoint site with FBA enabled. But the service reference is not working in the code. Do I need to create our own service ?

  • Anonymous
    October 21, 2010
    You do not need to create your own services. Which Service reference are you having trouble with? In this sample I hardcoded the authentication service in the code. You need to change this to point to your server. If you have done this, what is the error your are receiving. For exampl here is path to my server. System.Uri authServiceUri = new Uri("phone.contoso.com/.../authentication.asmx");

  • Anonymous
    October 22, 2010
    The comment has been removed

  • Anonymous
    October 24, 2010
    Can you please look at the below conversation and help me ? forums.create.msdn.com/.../391145.aspx

  • Anonymous
    October 24, 2010
    Can you please look at the below conversation and help me ? I am not getting resolved this problem. forums.create.msdn.com/.../391145.aspx

  • Anonymous
    November 12, 2010
    Hi Paul, I have two quick questions:

  1. Source Code: I downloaded the source, changed the relevant bits (username/password and url of sharepoint site), but when I run it I get an unhandled ArgumentNullException when calling GetTasks(), though it authenticates alright.  Am I missing something?  I put a sample task in the task list, so don't think it's that.
  2. I'm trying to use the same approach within a Silverlight 4 application, so have been translating your steps.  So far I've managed to solve a few issues by using the client HTTP stack (instead of the browser) and by adding enableHttpCookieContainer="true" to the client config file.  Now I can call Authenticate() and the OnAuthenticated event is raised, but when I call GetTasks() I get an authentication error. Any thoughts on what could be causing this? Many Thanks Tim
  • Anonymous
    November 14, 2010
    I have a 2010 server running in Claims mode with ADFS profiding the logon page. I can logon with Windows Phone 7 IE page but not with the Office Hub "Connecting" page. The username and password fields don't allow me to enter anything...

  • Anonymous
    February 01, 2011
    Hi Paul I hv followed your procedure but everytime I am getting ArgNullException. Can you help me ?

  • Anonymous
    February 01, 2011
    The comment has been removed

  • Anonymous
    February 20, 2011
    I got a null reference on this statement: HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult); I added my own service reference and changed the URL to my own SharePoint site but i can't seem to get it working. Do you have any idea where it's comming from?

  • Anonymous
    June 10, 2011
    Hi Paul Thanks for this great post. It helped me a lot. I managed to successfully cosume list data from SharePoint in a Windows Phone Panorama Application. Kind regards Bojan