claim based authentication CSOM

A few days ago I’ve been asked about how to access a SharePoint 2010 web site using CSOM, when the target Web Application is configured to use the claims-based authentication, there are multiple authentication providers configured, and you want to provide a FedAuth cookie automatically.
In fact, the ClientContext class provides the Credentials property or the FormsAuthenticationLogonInfo property, which together with the AuthenticationMode enum property (Default, FormsAuthentication, and Anonymous) allow to authenticate either using Windows credentials or FBA.
However, when you configure the claims-based authentication, you define multiple authentication providers, and you need to provide a FedAuth cookie to SharePoint via CSOM, the previously shown properties do not fit with your needs.
If you track the requests made from your browser while authenticating against a target SharePoint site, let’s say using Windows Integrated authentication and the claims-based authentication, you will see a flow like the following one:
 AuthN-Flow-Fiddler2
As you can see, your browser is redirected from the requested URL to the /_layouts/Authenticate.aspx page, which redirects (HTTP 302) your browser to the /_login/default.aspx page. This last page is the default login page, which prompts the end user with the authentication provider selector dropdown.
image
Let’s say you select the “Windows Authentication” option. As you can see in the Fiddler2 trace, your browser will be redirected again (HTTP 302) to the /_windows/default.aspx page. You will authenticate with your Windows credentials (eventually leveraging Integrated Authentication) and, as soon as you will be authenticated, your browser will be redirected (one more time!) to the /_layouts/Authenticate.aspx page, which will finally send one last redirect to the originally requested page. Under the cover, between steps 15 and 16 of the traced flow, SharePoint will emit a cookie (FedAuth) that will hold a reference to your authentication session. It will be something like that:
Set-Cookie: FedAuth=77u/PD94bWwg...; expires=Wed, 31-Jul-2013 07:34:25 GMT; path=/; HttpOnly
Every subsequent requests will provide the FedAuth cookie to SharePoint, in order to make it aware of the current authentication context.
In order to manually orchestrate such a flow, you will have to manually retrieve the FedAuth cookie and to provide it to the target Web Application via CSOM. Luckily, the ClientContext class provides an event handler called ExecutingWebRequest, which allows to intercept a web request running from the ClientContext to the target SharePoint site, just before the request is sent on the wire. Moreover, within the ExecutingWebRequest event you will get a variable of type WebRequestEventArgs, which provides you a hook to web request executor, the collection of HTTP Headers, the Cookies, etc.
Through the WebRequestExecutor property of the current event args, you will be able to access all the main information about the outgoing request, including the cookies. By providing a CookieContainer object to the WebRequest object used by the ClientContext you will be able to keep track of the issued FedAuth cookie and you will be able to authenticate against your target SharePoint.
In the following code excerpt you can see how to manage this task.
    // Create the ClientContext instance
    ClientContext ctx = new ClientContext(baseSiteUrl);

    // Configure anonymous authentication, because we will use FedAuth cookie instead
    ctx.AuthenticationMode = ClientAuthenticationMode.Anonymous;

    // Register an anonymous delegate to the ExecutingWebRequest event handler
    ctx.ExecutingWebRequest += new EventHandler<WebRequestEventArgs>((s, e) => {
        // If we do not have a cookies variable, which will be a shared instance of a CookieContainer
        if (null == cookies)
        {
            lock (cookiesSyncLock)
            {
                if (null == cookies)
                {
                    // Let’s create the CookieContainer instance
                    cookies = new CookieContainer();

                    // Make a “fake” request to the /_windows/default.aspx page
                    // emulating the flow previously illustrated
                    HttpWebRequest request = WebRequest.Create(
                        baseSiteUrl + "_windows/default.aspx?ReturnUrl=%2f_layouts%2fAuthenticate.aspx%3fSource%3d%252FDefault%252Easpx&Source=%2FDefault.aspx") as HttpWebRequest;
                    // Provide a set of Windows credentials (default or explicit)
                    request.Credentials = CredentialCache.DefaultNetworkCredentials;
                    request.Method = "GET";

                    // Assign the CookieContainer object
                     request.CookieContainer = cookies;
                    request.AllowAutoRedirect = false;

                    // Execute the HTTP request
                    HttpWebResponse response = request.GetResponse() as HttpWebResponse;
                    if (null != response)
                    {
                        // The following variable simply holds the FedAuth cookie value, but that value
                        // is not used directly
                        fedAuthCookieValue = response.Cookies[fedAuthCookieName].Value;
                    }
                }
            }
        }
        // Grab the CookieContainer, which now holds the FedAuth cookie, and configure
        // it into the WebRequest that the ClientContext is going to execute and …
        // you have done all you need!
        e.WebRequestExecutor.WebRequest.CookieContainer = cookies;
    });
    Site site = ctx.Site;
    Web web = ctx.Web;
    List targetList = web.Lists.GetByTitle("Shared Documents");
    ListItemCollection items = targetList.GetItems(CamlQuery.CreateAllItemsQuery());
    ctx.Load(items);
    ctx.ExecuteQuery();
    foreach (ListItem item in items)
    {
        Console.WriteLine(item["Title"]);
    }
}
That’s all! I hope this will help someone.

Comments