Webservice Calls with Windows Claims

We ran into a unique issue recently where we had a need to separate out application pool accounts but still needed to share data across web applications.  The hurdle here is that both applications were protected with claims based authentication using both Windows Claims and a third party Claims provider.

The idea to get around this is to use webservice calls with an elevated account from one web application to pull data from the other.  I’m sure, as with most things SharePoint there are a million and one ways to do this but this is what we went with and we were under a time crunch.

Great, this should be simple, lets just make an HttpWebRequest from one application to the other passing the credentials of the elevated account.  Not so much.  Every time we ran this code, it would just hit a brick wall, if the site was not warmed up, it would just time out.  If the site was warmed up we would get an exception that the target closed the connection.

After some searching I came across these two articlse.

http://msdn.microsoft.com/en-us/library/gg597521.aspx#SPS_LearningClaims_3_Tip2

http://blogs.technet.com/b/speschka/archive/2010/06/04/using-the-client-object-model-with-a-claims-based-auth-site-in-sharepoint-2010.aspx

The webservice call was a REST call, so we could test this in the browser and in doing so I was able to recreate the timeout/closed connection error.  I did notice that once logged in I was able to hit the URL fine.  I fired up fiddler to see if I could figure out what was different and I found that the difference between the requests was the FedAuth cookie mentioned in the above articles.

So how to do this with a set of Windows creds and the Windows claim provider.  The articles only outline how to go over this with an ADFS claims provider.  Back into fiddler, I took a look at the request/response where I first received the auth cookie.  Why not add a request to this url passing in our creds and see what we get.

Success!  You’ll find the code below used to test this out.  The missing piece:

http://hostname/_windows/default.aspx?ReturnUrl=/_layouts/Authenticate.aspx?Source=%252F&Source=/

The code:

static void Main(string[] args)
        {
            #region getAuth
            Console.WriteLine("Enter user domain");
            string domain = Console.ReadLine();
            Console.WriteLine("Enter username");
            string username = Console.ReadLine();
            Console.WriteLine("Enter password");
            string password = Console.ReadLine();

            NetworkCredential nc = new NetworkCredential(username, password, domain);
            CredentialCache ccCreds = new CredentialCache();
            ccCreds.Add(new Uri("http://hostname/"), "NTLM", nc);
            string FedAuth = "";
            try
            {
                Console.WriteLine("Authenticating");
                HttpWebRequest authReq = 
                     HttpWebRequest.Create("http://hostname/_windows/default.aspx?ReturnUrl=/"+
                     "_layouts/Authenticate.aspx?Source=%252F&Source=/") as HttpWebRequest;
                authReq.Method = "GET";
                authReq.Accept = @"*/*";
                authReq.CookieContainer = new CookieContainer();
                authReq.AllowAutoRedirect = false;
                //authReq.UseDefaultCredentials = true;
                authReq.UseDefaultCredentials = false;
                authReq.Credentials = ccCreds;
                HttpWebResponse webResponse = authReq.GetResponse() as HttpWebResponse;
                FedAuth = webResponse.Cookies["FedAuth"].Value;
                webResponse.Close();
            }
            catch (System.Net.WebException e)
            {
                if (e.Response != null)
                {
                    HttpWebResponse webResponse = e.Response as HttpWebResponse;
                    if (webResponse.StatusCode == HttpStatusCode.InternalServerError)
                    {
                        if ((e.Response as HttpWebResponse).Cookies != null)
                        {
                            FedAuth = webResponse.Cookies["FedAuth"].Value;
                        }
                    }
                    webResponse.Close();
                }
            }
            #endregion

            HttpWebRequest hwrTester = (HttpWebRequest)HttpWebRequest.Create("http://host/_vti_bin/listdata.svc");

            if (!String.IsNullOrEmpty(FedAuth))
            {
                Console.WriteLine("Auth found!");
                try
                {
                    hwrTester.Method = "GET";
                    hwrTester.Accept = @"*/*";
                    hwrTester.Headers.Add("Accept-Encoding", "gzip, deflate");
                    hwrTester.KeepAlive = true;
                    CookieContainer cc = new CookieContainer();
                    Cookie authcookie = new Cookie("FedAuth", FedAuth);
                    authcookie.Expires = DateTime.Now.AddHours(1);
                    authcookie.Path = "/";
                    authcookie.Secure = true;
                    authcookie.HttpOnly = true;
                    authcookie.Domain = hwrTester.RequestUri.Host;
                    cc.Add(authcookie);

                    hwrTester.CookieContainer = cc;
                    hwrTester.UseDefaultCredentials = true;
                    //hwrTester.UseDefaultCredentials = false;
                    hwrTester.Credentials = ccCreds;
                    hwrTester.Headers.Add("X-FORMS_BASE_AUTH_ACCEPTED", "f");

                    HttpWebResponse hwrespResp = (HttpWebResponse)hwrTester.GetResponse();
                    StreamReader data = new StreamReader(hwrespResp.GetResponseStream(), true);
                    string output = data.ReadToEnd();
                    data.Close();
                    hwrespResp.Close();
                    Console.WriteLine("Got response from list webservice!");
                    Console.Write(output);
                }
                catch (System.Net.WebException e)
                {
                    if (e.Response != null)
                    {
                        e.Response.Close();
                    }
                }
            }
        }

Leave a Reply