Search code examples
sharepointsharepoint-2010claims-based-identitysharepoint-clientobject

SharePoint 2010 Client Object Model - Kerberos/Claims Authentication


I'm trying to read a value from a list in a remote SharePoint site (different SP Web App). The web apps are set up with Claims Auth, and the client web app SP Managed account is configured with an SPN. I believe Kerberos and claims are set up correctly, but I am unable to reach the remote server, and the request causes an exception: "The remote server returned an error: (401) Unauthorized."

The exception occurs in the line ctx.ExecuteQuery(); but it does not catch the exception in the if (scope.HasException) instead, the exception is caught by the calling code (outside of the using{} block).

When I look at the traffic at the remote server using Wireshark, it doesn't look like the request is even getting to the server; it's almost as if the 401 occurs before the Kerberos ticket is exchanged for the claim.

Here's my code:

using (ClientContext ctx = new ClientContext(contextUrl))
{

    CredentialCache cc = new CredentialCache();
    cc.Add(new Uri(contextUrl), "Kerberos", CredentialCache.DefaultNetworkCredentials);
    ctx.Credentials = cc;
    ctx.AuthenticationMode = ClientAuthenticationMode.Default; 

    ExceptionHandlingScope scope = new ExceptionHandlingScope(ctx);
    Web ctxWeb = ctx.Web;
    List ctxList;
    Microsoft.SharePoint.Client.ListItemCollection listItems;

    using (scope.StartScope())
    {
        using (scope.StartTry())
        {

            ctxList = ctxWeb.Lists.GetByTitle("Reusable Content");
            CamlQuery qry = new CamlQuery();
            qry.ViewXml = string.Format(ViewQueryByField, "Title", "Text", SharedContentTitle);
            listItems = ctxList.GetItems(qry);
            ctx.Load(listItems, items => items.Include(
                                item => item["Title"],
                                item => item["ReusableHtml"],
                                item => item["ReusableText"]));
        }
        using (scope.StartCatch()) { }
        using (scope.StartFinally()) { }
    }
    ctx.ExecuteQuery();

    if (scope.HasException)
    {
        result = string.Format("Error retrieving content<!-- Error Message: {0} | {1} -->", scope.ErrorMessage, contextUrl);
    }


    if (listItems.Count == 1)
    {
        Microsoft.SharePoint.Client.ListItem contentItem = listItems[0];

        if (SelectedType == SharedContentType.Html)
        {
            result = contentItem["ReusableHtml"].ToString();
        }
        else if (SelectedType == SharedContentType.Text)
        {
            result = contentItem["ReusableText"].ToString();
        }
    }


}

I realize the part with the CredentialCache shouldn't be necessary in claims, but every single example I can find is either running in a console app, or in a client side application of some kind; this code is running in the codebehind of a regular ASP.NET UserControl.

Edit: I should probably mention, the code above doesn't even work when the remote URL is the root site collection on the same web app as the calling code (which is in a site collection under /sites/)--in other words, even when the hostname is the same as the calling code.

Any suggestions of what to try next are greatly appreciated!

Mike


Solution

  • Is there a reason why you are not using the standard OM?

    You already said this is running in a web part, which means it is in the context of application pool account. Unless you elevate permissions by switching users, it won't authenticate correctly. Maybe try that. But I would not use the client OM when you do have access to the API already.