Retrieving Data from a Multi Auth Site Using the Client OM and Web Services in SharePoint 2010

Just the subject line of this post is a mouthful, if it’s any indication of what we’re going to be covering today. This post is going to address an issue that has long bothered myself and others. I just recently started asking around about it and then coincidentally got an email from someone that had just received info on how to resolve this issue.

 

I’m only covering the most common parts of this scenario; there are multiple twists that can be added to them. The problem is retrieving data from a SharePoint site that is using multiple authentication providers; in this scenario, assume one is Windows claims and the other is anything else – it could be Forms Based auth or SAML auth. Frequently you will want to retrieve data from such a site using either the Client OM or SharePoint web services but use Windows authentication to do so. The problem up to this point has always been that even when you set your Windows credentials on the request, you still got an Access Denied when requesting the data.

 

The underlying resolution to this problem is that if you want to programmatically access a SharePoint site that uses multiple auth providers with a set of Windows credentials, you need to add an additional header to your request. The header name needs to be X-FORMS_BASED_AUTH_ACCEPTED and the value needs to be “f”. Adding this header can be a little less than straightforward for these two common scenarios so the rest of the post will explain how to do that with some sample code.

 

If you’re using the Client OM, you need to add an event handler for the ExecutingWebRequest event. Here’s an example of the code and what it looks like:

 

//create the client context

ClientContext ctx = new ClientContext(MixedUrlTxt.Text);

 

//configure the handler that will add the header

ctx.ExecutingWebRequest +=

new EventHandler<WebRequestEventArgs>(ctx_MixedAuthRequest);

 

//set windows creds

ctx.AuthenticationMode = ClientAuthenticationMode.Default;

ctx.Credentials = System.Net.CredentialCache.DefaultCredentials;

 

//get the web

Web w = ctx.Web;

 

//LOAD LISTS WITH ALL PROPERTIES

var lists = ctx.LoadQuery(w.Lists);

 

//execute the query

ctx.ExecuteQuery();

 

//enumerate the results

foreach (List theList in lists)

{

//do something with each list

}

 

And this is where the magic happens:

 

void ctx_MixedAuthRequest(object sender, WebRequestEventArgs e)

{

try

{

              //add the header that tells SharePoint to use Windows Auth

              e.WebRequestExecutor.RequestHeaders.Add(

"X-FORMS_BASED_AUTH_ACCEPTED", "f");

       }

       catch (Exception ex)

       {

              MessageBox.Show("Error setting auth header: " + ex.Message);

       }

}

 

That’s all there is too it; that one is actually pretty easy and I think self-explanatory. Doing the same thing with a standard Web Service reference is a little different. To begin with, let’s walk through the process of adding a standard style web reference in a SharePoint site to a project in Visual Studio 2010:

1. Right-click the Service Reference node and select Add Service Reference.

2. Click the Advanced button on the bottom of the dialog.

3. Click the Add Web Reference… button on the bottom of the next dialog.

4. Enter the Url to the web service you want to use in the URL edit box; for example to add a reference to the Lists web service in the site at https://foo I would put this in for the URL: https://foo/_vti_bin/lists.asmx.

5. Hit Enter or click the green arrow button to find the web service reference, then type in a name for your web service reference in the Web reference name edit box and click the Add Reference button.

 

Your reference and proxy classes for your reference should be created now, but you’ll need to add an additional partial class in order to add the header to your web service request. Start by adding a new class to your project, and call it whatever you want. Since you just want to add some additional behavior – adding a header to the request – you’ll want to make it a partial class. That means that you need to copy both the namespace and class name used in the proxy created for your web reference. Here are the steps to do that:

 

1. Click the Show All Files button in the Solution Explorer window in Visual Studio.

2. Click the plus sign next to your web service reference to expand it.

3. Click the plus sign next to the Reference.map file to expand it.

4. Double-click the Reference.cs file to open it.

5. Copy the namespace and paste it into your class.

6. Copy the class name, including inheritance, and paste it in your class as your class name. The web service reference class is a partial class already so there isn’t any changes you’ll need to do there.

 

Here’s an example of what my the Reference.cs class looked like for my web service reference:

namespace ClientOmAuth.listsWS {

    using System;

    using System.Web.Services;

    using System.Diagnostics;

    using System.Web.Services.Protocols;

    using System.ComponentModel;

    using System.Xml.Serialization;

   

   

    /// <remarks/>

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.0.30319.1")]

    [System.Diagnostics.DebuggerStepThroughAttribute()]

    [System.ComponentModel.DesignerCategoryAttribute("code")]

    [System.Web.Services.WebServiceBindingAttribute(Name="ListsSoap", Namespace="https://schemas.microsoft.com/sharepoint/soap/")]

    public partial class Lists : System.Web.Services.Protocols.SoapHttpClientProtocol {

 

And here’s what it I pasted into my class that I created:

namespace ClientOmAuth.listsWS

{

       public partial class Lists : System.Web.Services.Protocols.SoapHttpClientProtocol

       {

       }

}

 

So you’ll notice how both the namespace and class names match, and that both classes are inheriting from the same base type.

 

Now you need to override the GetWebRequest method so you can add the header. That is done simply by adding this code to your partial class:

 

protected override System.Net.WebRequest GetWebRequest(Uri uri)

{

       System.Net.WebRequest wr = null;

 

       try

       {

              wr = base.GetWebRequest(uri);

              wr.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");

       }

       catch (Exception ex)

       {

              //some error handling here

       }

                          

       return wr;

}

 

 

Coding to retrieve the data via the web service is now exactly the same as you would with any Windows auth SharePoint site:

 

//create the web service proxy and configure it use my Windows credentials

listsWS.Lists lws = new listsWS.Lists();

lws.UseDefaultCredentials = true;

 

//get the collection of lists

XmlNode xLists = lws.GetListCollection();

 

//enumerate results

foreach (XmlNode xList in xLists.ChildNodes)

{

//do something with each List

}

 

That’s all there is too it. You can apply this information to retrieving data via REST as well, using the techniques I described here: https://blogs.technet.com/b/speschka/archive/2010/09/25/retrieving-rest-data-in-a-claims-based-auth-site-in-sharepoint-2010.aspx.

 

Retrieving Data from a Multi Auth Site Using the Client OM and Web Services in SharePoint 2010.docx