Sudhindra Kovalam's Blog

My Geeky Blog

Writing windows phone apps for SharePoint

with 6 comments

Writing a blog post after quite some time. (Well frankly was thinking on what sort of post to write)

We have heard / seen / read about how windows phone has good SharePoint 2010 integration features as to how the office hub can integrate with your corporate intranet SharePoint site and how, you can leverage the SharePoint workspace capabilities in windows phone.

after reading about this, you’d say cool, can i write an app which speaks to my SharePoint site. Well the answer is Yes. But there are some changes that you need to to do on SharePoint site configuration.

“Argh!!, making changes on SharePoint site, My boss(and/or system admin) will kill me! “.

Was this your reaction ? (at least, that was my initial reaction).

In that case, this post might not be of that much help.

However, we can always host a cloud service that would help you get data from SharePoint and you can consume the data from this “middle man” cloud service. ( Off course this does add up expenses that at the end of the day needs to be taken care off by some one)

So, why is that I cannot consume SharePoint service directly from my windows phone.

Well it all began, when windows phone SDK team decided to omit NTLM auth support from the SDK, thus preventing us 3rd party developers from directly writing cool apps that speak to your SharePoint site.

“Why did they do this to us? “ you ask!!, Well frankly, i don’t have an answer to that.

But, There is a way out of this. How is that? Well , all you got to do is enable Forms based authentication for your SharePoint site.

How do I do that? There is a good article here. (Remember, you need to be very very strong hearted to take up all these changes, as these changes might literally take down your SharePoint site. )

Here’s a pictorial representation of auth mechanisms and SharePoint support for the same.

(Stolen from Paul Stubbs blog on MSDN, Sorry!! )

5488.image_thumb_0D9FC62A

Well what story, the above diagram is trying to tell is:

SharePoint 2010 supports two different authentication modes.

1. Classic mode (The Loner left side branch, support NTLM authentication only)

2. Claims based mode (The right side branch of the diagram above)

What you can realise is that if you use the claims based mode for creating your SharePoint web application, you can use any of the claims providers to authenticate yourself (in this case, your phone app’s code) to your SharePoint site. (Off course, your system administrator can have hard time configuring the same, but lets not get into that Open-mouthed smile)

Once you have the infrastructure in place. All you got to do is use the SharePoint 2010 Web services to access SharePoint lists etc.

In SharePoint, you cannot make any changes to a spsite until you are an authenticated user. So You need to log in programmatically to make changes such as adding list items, modifying ‘em etc.

For Logging in , you can use the authentication.asmx web service to authenticate yourself.

(the spsite you are trying to access via your code needs to allow form based authentication.) The Authentication site is available at : http://[YourSiteName]/_vti_bin/authentication.asmx

Logging into SharePoint

Here’s what happens when you visit a SharePoint site: (Again stolen for Paul stubbs’ blog , sorry again!!)

4606.image_4D69ACAF

1. you visit a SharePoint site page.

2. you are redirected to the login page(this internally uses the authentication web service), you enter your credentials here.

3. the authentication service returns a security token(FEDAUTH).

4. this security token is passed for all subsequent calls to the SharePoint site.

5. this security token is verified by the server for validity and then the server returns the requested resource.

How Do I do this Programatically?

Well, for programmatically mimicking the above mentioned behaviour, you can write  code that looks something like this:

CookieContainer cookieJar = new CookieContainer();
    private void Authenticate()
    {
      System.Uri authServiceUri = new Uri("http://[YourSPSite]/_vti_bin/authentication.asmx");

      HttpWebRequest spAuthReq = HttpWebRequest.Create(authServiceUri) as HttpWebRequest;
      spAuthReq.CookieContainer = cookieJar;
      spAuthReq.Headers["SOAPAction"] = "http://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=""http://www.w3.org/2001/XMLSchema-instance""
               xmlns:xsd=""http://www.w3.org/2001/XMLSchema""
                xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
          <soap:Body>
            <Login xmlns=""http://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", "password");
      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
      //Use the Cookie Container for all subsequent calls
    }

 

Now that you have the FEDAUTH token in the CookieContainer instance, you can pass it around to access other list services.

System.Uri listServiceUri = new Uri("http://[YourSpSite]/_vti_bin/lists.asmx");
HttpWebRequest spAuthReq = HttpWebRequest.Create(listServiceUri) as HttpWebRequest;
spAuthReq.CookieContainer = cookieJar;

 

Will try and post up a complete working sample of the same very very soon! Till then keep checking this space for more updates Smile

Written by sudheerkovalam

November 4, 2010 at 3:03 pm

Posted in Application Design, Windows Phone 7

Tagged with

6 Responses

Subscribe to comments with RSS.

  1. […] This post was mentioned on Twitter by Mayur Tendulkar and Ajay Nayak, Sudhindra Kovalam. Sudhindra Kovalam said: Writing windows phone apps for SharePoint: http://wp.me/pA3Y0-2Y #wp7dev #sp2010 #FBA […]

  2. Can we have the detailed on the same please…
    I have tried the above code “The remote server returned an error: (500) Internal Server Error.”

    Can you please help us out ..?

    Parth

    Parth

    April 7, 2011 at 1:04 pm

  3. Sudhir,
    I am having issues(access denied).. I am attaching my cod.. i am trying add users to Claims authenticated site 2010

    private bool AddUsertoSPGroup(string groupname, string username, string userLoginName, string userEmail, string userNotes)
    {

    using (server_UsrGrp_WebSvc.UserGroup userGroup = new server_UsrGrp_WebSvc.UserGroup())
    {
    try
    {
    string _usernameWithProvider = String.Format(“{0}:{1}”, System.Web.Security.Membership.Provider.Name, username);
    string Url = “http://server:61000”;

    userGroup.Credentials = new NetworkCredential(“login”, “pwd”, “domain”);
    //userGroup.Credentials =System.Net.CredentialCache.DefaultCredentials;

    userGroup.CookieContainer = GetFbaAuthenticationCookie();
    userGroup.Url = Url + @”/_vti_bin/UserGroup.asmx”;

    userGroup.AddUserToGroup(groupname, _usernameWithProvider, userLoginName, userEmail, userNotes);

    /*Declare an XmlNode object and initialize it with the XML response
    from the GetUserCollectionFromGroup method, declare a second XmlNode
    object and initialize it with the first child of the first node
    returned, and then declare and initialize an XmlNodeList object with
    the child nodes of the second node.*/
    System.Xml.XmlNode usersNode1 =
    userGroup.GetUserCollectionFromGroup(groupname);
    System.Xml.XmlNode usersNode2 = usersNode1.FirstChild;
    System.Xml.XmlNodeList userNodes = usersNode2.ChildNodes;

    /*Iterate through the collection of user nodes and parse out the
    Name, LoginName, Email, and Notes attribute values for each item and
    then display the values returned.*/
    foreach (System.Xml.XmlNode user in userNodes)
    {
    string name = user.Attributes[“Name”].Value;
    string loginName = user.Attributes[“LoginName”].Value;
    string email = user.Attributes[“Email”].Value;
    string notes = user.Attributes[“Notes”].Value;

    Console.WriteLine(“\n\n” + name + ” : ” + loginName + ” : ” + email + ” : ” + notes);
    }

    return true;
    }
    catch (Exception ex)
    {
    Console.WriteLine(ex.Message);
    return false;
    }
    }
    }
    private System.Net.CookieContainer GetFbaAuthenticationCookie()
    {
    server_auth_websvc.Authentication fbaAuth = null;

    try
    {
    fbaAuth = new server_auth_websvc.Authentication();
    fbaAuth.CookieContainer = new System.Net.CookieContainer();
    fbaAuth.AllowAutoRedirect = true;
    cmhdspw01_auth_websvc.LoginResult loginResults = fbaAuth.Login(“login”, “pwd”);

    if (loginResults.ErrorCode != cmhdspw01_auth_websvc.LoginErrorCode.NoError)
    {
    // throw exception
    //throw new Exception(“Authentication Service Login Error.”);
    }
    }
    catch (Exception ex)
    { /* Custom Logging */
    Console.WriteLine(ex.Message);
    }

    return fbaAuth.CookieContainer;
    }

    neel

    April 21, 2011 at 9:45 pm

  4. Hi,
    Good post, I’ve also been working on a problem for which using the Authentication.asmx service seems to be the way to best get Claims users logged in. In my case it’s a simple webpart calling a WCF service, nothing complex.

    However in your example above as with others I have found you have the username and password in the code, is this the only way? What I really need to do is get the login cookie (or claim token) from the currently logged in user and use that.

    Any ideas?

    Thanks,

    Martin

    May 12, 2011 at 12:10 am

    • Well actually, I too am looking to get the same to work (by using the Current Logged in User Claim Token)
      There are some good example on how to do this against Google accounts and Live Accounts.
      The Project is hosted on Codeplex and it has code samples http://claimsid.codeplex.com/
      this should help you understand 🙂

      sudheerkovalam

      May 12, 2011 at 3:24 am

  5. Thanks Sudhindra,

    This is helpful.

    Jayvardhan Patil

    February 7, 2012 at 6:30 pm


Leave a reply to neel Cancel reply