PortiBlog

Consuming a web service with Claims Based Authentication and ADFS programmatically

4 december 2014

The problem
Many of our customers are nowadays using Authentication in combination with ADFS (Active Directory Federation Services). The result is an increased complexity, especially if you want to approach resources from third party sites. For example: getting data by using a web service.

In our situation, we try to connect a SharePoint 2013 web service to get access to some product data. In this blog, we will describe every step to accomplish our mission. We used Fiddler, a custom console application and code snippets to build this blog.

Authors of this blog: Marcel Buckens, Consultant at Portiva & Theo de Groot, Developing Consultant at Portiva.

SharePoint Infrastructure with ADFS
In the picture below you’ll see the “basic” infrastructure. Of course there are more servers, routers, switches etcetera but for our example just fine.
First of all we have our client. If this is a server or client pc doesn’t change much for this example. Basically you’re connecting from one place to another and you need to authenticate before you’re allowed to receive any data from the source. In our case we are connecting to SharePoint 2013 on-premises, running in Microsoft Azure, which is using a corporate ADFS. The example works fine in our situation. For connecting to another infrastructure (for example O365), you have to change to code to that particular situation. The idea will be still the same.

Basis_Infra-300x256

Step 1 - Starting Console Application to test consuming a web service
We created this test with Visual Studio 2013. First we start a new Console Application. We used the name 'TestWebServiceWithADFS'.

blog1

Add the references below. As you can see, we used a Codeplex assembly (HtmlAgilityPack), which can be downloaded at http://htmlagilitypack.codeplex.com/. This assembly makes it possible to use HtmlDocument (var htmlDocument = new HtmlDocument();)

blog2

Add a Web Reference to the web service you want to approach.

blog4

Look at the Program.cs file. This is the place where we implement the code below.

blog3

Step 2 - Starting the particular SharePoint Site
In this step, we will access a SharePoint Site with Claims based authentication in the particular SharePoint farm. Replace the Login, Password and URI in the code below and put the code in the MAIN method.

20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
string username = "yourLogin@yourDomain";
string password = "yourPassword"; CookieContainer cookies = new CookieContainer();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
string message = string.Empty; try
{
Uri listUri = new Uri("https://targetSite.com/_trust/default.aspx");
HttpWebRequest request = WebRequest.Create(listUri) as HttpWebRequest;
request.AllowAutoRedirect = false;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();

// } catch {} later in this blog

You wil get a response back after this request as shown in the Fiddler output below.
Pic1-800

Step 3 - Redirection to ADFS
The respons from step 2 has a property called 'StateCode'. In a situation with ADFS, the value of this property will be 'HttpStatusCode.Redirect'. Now we have to implement the code to redirect to the ADFS server. Put this code below the code of step 2.

34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
string url = string.Empty;
string domain = listUri.Host;
string userAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"; while (response.StatusCode == HttpStatusCode.Redirect)
{
url = response.Headers[HttpResponseHeader.Location]; request = WebRequest.Create(url) as HttpWebRequest;
request.AllowAutoRedirect = false;
request.UserAgent = userAgent;
response = (HttpWebResponse)request.GetResponse();
}

You wil get a response back after this request as shown in the Fiddler output below.
Pic2-800

Step 4 - Login
Now we are redirected, we have to login to the ADFS server. This is possible with posting the login and password. Put the code below in your application

49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
// Create the form data to post back to the server
NameValueCollection namevalues = GetHTMLInputTags(ResponseToString(response));
string postData = string.Empty;postData += "UserName=" + System.Web. HttpUtility.UrlDecode(username) + "&";
postData += "Password=" + System.Web. HttpUtility.UrlEncode(password);
postData += "&AuthMethod=FormsAuthentication"; // Submit the data back
cookies.Add(response.Cookies);

request = WebRequest.Create(url) as HttpWebRequest;
request.CookieContainer = cookies;
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postData.Length;
request.Method = "POST";
// Important: we need to handle the redirect ourselves
request.AllowAutoRedirect = false;

// post the data to the request
using (StreamWriter sw = new StreamWriter(request.GetRequestStream()))
{
sw.Write(postData); sw.Flush(); sw.Close();
}

After authentication, the ADFS server will send you a cookie with a SAML message. In step 5, we will use this cookie.

Step 5 - SAML
In step 4, you will get a cookie response as shown in the Fiddler output below.
Pic3-800

Now we have to get this cookie and save the SAML message in our cookie container. Implement the code below.

74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
// Important to get cookies here
response = ( HttpWebResponse)request.GetResponse();
cookies.Add(response.Cookies); while (response.StatusCode == HttpStatusCode.Redirect)
{
url = response.Headers[HttpResponseHeader.Location]; request = WebRequest.Create(url) as HttpWebRequest;
request.CookieContainer = cookies;
request.AllowAutoRedirect = false;

response = (HttpWebResponse)request.GetResponse();
cookies.Add(response.Cookies);
}

Step 6 - Login to SharePoint
With the SAML cookie of step 5, we can Login to the SharePoint Site of step 2. This is only possible by posting an encoded cookie. Implement the code below.

90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
string rootUrl = string.Format( "{0}://{1}", listUri.Scheme, listUri.Host);
request = WebRequest.Create(rootUrl) as HttpWebRequest;
request.CookieContainer = cookies;
request.AllowAutoRedirect = false;
request.UserAgent = userAgent;
response = ( HttpWebResponse)request.GetResponse();
cookies.Add(response.Cookies); while (response.StatusCode == HttpStatusCode.Redirect)
{
url = response.Headers[HttpResponseHeader.Location];
if(url.StartsWith("/"))
{
url = rootUrl + url;
}
request = WebRequest.Create(url) as HttpWebRequest;
request.CookieContainer = cookies;
request.UserAgent = userAgent;
request.AllowAutoRedirect = false; response = (HttpWebResponse)request.GetResponse();
cookies.Add(response.Cookies);
}

url = rootUrl + "/_trust/default.aspx";
namevalues = GetHTMLInputTags(ResponseToString(response));

string stringData = string.Format("wctx={0}&wresult={1}&wa=wsignin1.0",
System.Web.HttpUtility.UrlEncode(namevalues["Wctx"]),
System.Web.HttpUtility.UrlEncode(namevalues["wresult"].Replace(
"&quot;",@"""").Replace("&lt;","<")) );

request = HttpWebRequest.Create(url) as HttpWebRequest;

request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.CookieContainer = cookies;
request.UserAgent = userAgent;
request.AllowAutoRedirect = false;
// Important: we need to handle the redirect ourselves
Stream newStream = request.GetRequestStream();

// Actually send the request
using (StreamWriter sw = new StreamWriter(newStream))
{
sw.Write(stringData); sw.Flush(); sw.Close();
}

In the Fiddler output below, we show you the request we did.
Pic4-800

Step 7 - Get FedAuth cookie
The request of step 6 will result in a response. With this response, a 'FedAuth' cookie will be send. We have to get and save this FedAuth cookie. Implement the code below.

138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.150.
151.
152.
153.
154.155.
response = request.GetResponse() as HttpWebResponse;
//get the fed-auth cookie
if (response.Cookies.Count > 0)
{
var fedAuth = response.Cookies["FedAuth"];
if (fedAuth != null)
{
cookies.Add(fedAuth);
}
else
{
throw new ApplicationException("FedAuth cookie not received from SharePoint Please contact SharePoint administrator for further assistance.");
}
}
else
{
throw new ApplicationException("FedAuth cookie not received from SharePoint. Please contact SharePoint administrator for further assistance.");
}

Step 8 - Open SharePoint Site
Now we have a FedAuth cookie, we can open the SharePoint Site. Implement the code below.

157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
while(response.StatusCode == HttpStatusCode.Redirect)
{
url = response.Headers[HttpResponseHeader.Location];
if(url.StartsWith("/"))
{
url = rootUrl + url;
}
request = WebRequest.Create(url) as HttpWebRequest;
request.CookieContainer = cookies;
request.AllowAutoRedirect = false; response = (HttpWebResponse)request.GetResponse();
cookies.Add(response.Cookies);
}
// Do webrequest with cookie.}
finally
{
// Delete cookie and signout ( ?wa=wsignoutcleanup1.0 )
}

We are in!

Step 9 - Implement needed methods
In the code, we use two methods. Of course these methods has to be implemented also in the Program.cs file.

180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
public static string ResponseToString( HttpWebResponse response)
{
// Get the stream containing content returned by the server and.
// Open the stream using a StreamReader for easy access.
using(StreamReader reader = newStreamReaderresponse.GetResponseStream()))
{
// Read the content.
return reader.ReadToEnd();
}
}
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.200.
201.
202.
203.
204.
205.
206.207.

208.
209.
210.
211.
212.
213.
214.
215.

public static NameValueCollectionGetHTMLInputTags( string responseString)
{
varhtmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(responseString);
var nodeCollection = htmlDocument.DocumentNode.SelectNodes("//input");
var tags = new NameValueCollection();
for (var index = 0; index < nodeCollection.Count; index++)
{
var attributeCollection = nodeCollection[index].Attributes;
vartype = attributeCollection["type"] != null ? attributeCollection["type"].Value :
string.Empty;
if (type.ToLower().Equals("submit") || type.ToLower().Equals("button"))
{
continue;
} var name = attributeCollection["name"] != null ? attributeCollection
["name"].Value :string.Empty;
var value = attributeCollection["value"] != null ? attributeCollection
["value"].Value : string.Empty;

if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))
{
tags.Add(name, value);
}
}
return tags;
}

You can run the Console Application right now. In step 10, we will implement a method to call the web service.

Step 10 - Call web service
This step is optional to explain your call to the web service.
First: change the URI in step 2 to access the web service

30.
HttpWebRequest request = WebRequest.Create( new Uri( "https://targetSite.com/_vti_bin/testservice.asmx")) as HttpWebRequest;

Second: Call including the FedAuth cookie

 
public void CallWebService( CookieContainer cookies)
{
testservice.NameWebService spf = new testservice.NameWebService();
spf.CookieContainer = cookies;
ClassNameWebService[] results =
spf.NameWebMethodWebService(Optional_Parameter, Optional_Parameter2);
foreach (var product in results)
{
string testing = product.title;
Console.WriteLine(testing);
//.......
}
}

 

Submit a comment