-->
");
One of our client upgraded their SSO authentication mechanism for their site from SiteMinder v1 to SiteMinder v2. We have built one project for this client that was making use of SSO to authenticate users and allowing them access. With WSS 3.0 and SiteMinder v1, we didn’t had much issues. We passed the username password, got hold of the SAML token and used it in the cookie container to consume the service from WSS 3.0.
But with sharepoint 2010 (claims enabled) and SiteMinder v2.0, there were a lot of difference. There were around 7-8 redirects before we hit the login page. Then we constructed the data to be posted and posted to the login page. Got the SAML token but it’s not finished yet. There were again a series of redirects before we hit the _Trust link. The trust link is where we post the web context, web result and web authentication data. Successfully posting these 3 data along with required cookies helped to get hold of the much wanted FedAuth cookie. This is the cookie that will help you consume services hosted at sharepoint 2010. Below is the full listing of the code.
public static void UpdateListItem(string listPath, string username, string password)
{
CookieContainer cookies = new CookieContainer();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
string message = string.Empty;
try
{
// Open connection to Protected URI. This will be intercepted and redirected
// to SiteMinder login screen.
Uri listUri = new Uri(listPath);
HttpWebRequest request = WebRequest.Create(listUri) as HttpWebRequest;
request.AllowAutoRedirect = false;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string url = string.Empty;
string domain = listUri.Host; //"https://yourdomain.com";
string userAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"; //very important to set the agent parameter to requests
Cookie jcookie = null;
while (response.StatusCode == HttpStatusCode.Redirect)
{
url = response.Headers[HttpResponseHeader.Location];
//check headers for Set-Cookie
if (!string.IsNullOrEmpty(response.Headers[HttpResponseHeader.SetCookie]))
{
string[] cookie = response.Headers[HttpResponseHeader.SetCookie].Split(';');
string[] cookieNameValue = cookie[0].Split('=');
jcookie = new Cookie(cookieNameValue[0], cookieNameValue[1]);
jcookie.Domain = listUri.Host;
string[] cookiePathValue = cookie[1].Split('=');
jcookie.Path = cookiePathValue[1];
string cookieIsSecure = cookie[2];
if (cookieIsSecure.Trim().ToLower() == "secure")
jcookie.Secure = true;
cookies.Add(jcookie); // This is one of the cookie that is required when posting data to trust link. Verified using fiddler
}
request = WebRequest.Create(url) as HttpWebRequest;
request.AllowAutoRedirect = false;
request.UserAgent = userAgent;
response = (HttpWebResponse)request.GetResponse();
}
// Create the form data to post back to the server
NameValueCollection namevalues = GetHTMLInputTags(ResponseToString(response));
string postData = string.Empty;
foreach (string key in namevalues.Keys)
{
postData += key + "=" + System.Web.HttpUtility.UrlEncode(namevalues[key]) + "&";
}
postData += "postpreservationdata=&";
postData += "userid=" + System.Web.HttpUtility.UrlDecode(username) + "&";
postData += "password=" + System.Web.HttpUtility.UrlEncode(password);
// Submit the data back to SiteMinder
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();
}
// Important to get the cookies here (they will include the SMSESSION cookie)
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);
}
#region Manual Authenticate
string rootUrl = string.Format("{0}://{1}", listUri.Scheme, listUri.Host);// "https://mydomain.com";
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;
if (url.Contains("affwebservices"))
cookies.Add(jcookie); // This is the place we add the jsession cookie (seems optional)
request = WebRequest.Create(url) as HttpWebRequest;
request.CookieContainer = cookies;
request.UserAgent = userAgent;
request.AllowAutoRedirect = false;
response = (HttpWebResponse)request.GetResponse();
cookies.Add(response.Cookies);
}
#region Manual STS Post to Trust
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(""", @"""")));
request = HttpWebRequest.Create(url) as HttpWebRequest;
Console.WriteLine(string.Format("Programmatic
Post to URL = {0}
", request.RequestUri));
", request.RequestUri));
Console.WriteLine(string.Format("Post
Data = {0}
", stringData));
", stringData));
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.CookieContainer = cookies;
request.UserAgent = userAgent;
request.AllowAutoRedirect = false; // This is important
Stream
newStream = request.GetRequestStream();
//
Actually send the request
using
(StreamWriter sw = new
StreamWriter(newStream))
{
sw.Write(stringData);
sw.Flush(); sw.Close();
}
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
{
cookies.Add(response.Cookies);
}
}
else
if (!string.IsNullOrEmpty(response.Headers[HttpResponseHeader.SetCookie]))
{
string[]
cookie = response.Headers[HttpResponseHeader.SetCookie].Split(';');
Cookie
fedCookie = null;
for
(int index = 0; index < cookie.Length;
index++)
{
string[]
cookieNameValue = cookie[index].Split('=');
switch (cookieNameValue[0].ToLower())
{
case "fedauth":
fedCookie = new Cookie(cookieNameValue[0],
cookieNameValue[1]) { Domain = domain.Replace("https://",
string.Empty) };
break;
case "expires":
fedCookie.Expires = Convert.ToDateTime(cookieNameValue[1]);
break;
case "path":
fedCookie.Path
= cookieNameValue[1];
break;
case "secure":
fedCookie.Secure = true;
break;
}
}
cookies.Add(fedCookie);
}
else
{
throw
new ApplicationException("FedAuth cookie not received from Sharepoint. Please
contact sharepoint administrator for further assistance.");
}
#endregion
//
Get the page we should hit next (should be Cognos)
//
Step 7: Persist the cookie
while
(response.StatusCode == HttpStatusCode.Redirect)
{
url = response.Headers[HttpResponseHeader.Location];
Console.WriteLine(string.Format("Status
= {0}, Response URL = {1}
", response.StatusDescription, url));
", response.StatusDescription, url));
if
(url.StartsWith("/"))
url = rootUrl + url;
request = WebRequest.Create(url) as
HttpWebRequest;
request.CookieContainer =
cookies;
request.AllowAutoRedirect =
false;
Console.WriteLine(string.Format("Request
URL = {0}
", request.RequestUri));
", request.RequestUri));
response = (HttpWebResponse)request.GetResponse();
cookies.Add(response.Cookies);
}
var
lists = new Lists
{ CookieContainer = cookies };
#region Code to update metadata for
"My Submitted Content" list
var
camlcmd = new StringBuilder();
camlcmd.Append("" );
foreach
(var field in
mmsParams.ListItemFields)
{
camlcmd.AppendFormat
(
"{1} ",
field.Key.Replace(" ", "_x0020_"),
field.Value
);
}
camlcmd.Append("
var
updates = (new XmlDocument()).CreateElement("Batch");
updates.InnerXml =
camlcmd.ToString();
var
resp = lists.UpdateListItems(mmsParams.PublishListName, updates);
#endregion
#region List Service response
if
(resp.FirstChild.FirstChild.InnerText == "0x00000000")
{
var
listitemid = resp.ChildNodes[0].ChildNodes[2].Attributes["ows_ID"].Value;
foreach
(string attachment in
mmsParams.Attachments)
{
lists.AddAttachment
(
mmsParams.PublishListName,
listitemid,
Path.GetFileName(attachment),
ReadBytes(attachment)
);
}
}
else
{
throw
new Exception("No field was found with that name. Check the name,
and try again");
}
#endregion
}
finally
{
//
Step 9: Delete the cookie from cache when done
//DeletePersistentCookies(cookies);
}
}
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 = new StreamReader(response.GetResponseStream()))
{
//
Read the content.
return
reader.ReadToEnd();
}
}
public static NameValueCollection
GetHTMLInputTags(string responseString)
{
var
htmlDocument = 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;
var
type = 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;
}
9 comments:
Hi, I think this is a very nice post but you missing this functions
can you post it?
GetHTMLInputTags(ResponseToString(response));
Hi,
Sorry for the delayed response. Now I edited the post and added the missing 2 methods.
Thanks,
Hemant.
Hi Hemant,
I am working on a sharepoint 2010 project that is currently using active directory based authentication. Now we are migrating that to the Siteminder. I need some help from you about the development aspects of it. I didn't find any materials on the internet for the same.
I saw your blog and i hope you have some good experiance in Siteminder integration. If you have a any knowledge base or some best practices guide, could you please share that in your blog?
Also where to start in Siteminder integration with sharepoint would also help everyone.
Hi Sun,
This resource on this subject is quite trickier or rather i would say tedious to find in the web but here are the resources I have used: Hope this helps you get upto the speed.
1. http://www.codeproject.com/Articles/80314/How-to-Connect-to-a-SiteMinder-Protected-Resource
2. http://msdn.microsoft.com/en-us/library/gg317440.aspx - On the left side you will see some example. You can review those to get good understanding about programmatic authentication.
As far as I know, the important thing to get hold of is the "FedAuth" cookie.
Thanks,
Hemant.
Is this code complete?
The first method contains an unclosed try without a catch statement, and the method itself unclosed.
Just wondering if there is more important stuff before the end of the try block.
Yes Rob,
You are correct. Now I have updated the method. Thanks for pointing this out.
Regards,
Hemant.
Hi Hemant,
I need to load the Sharepoint online page(https://mysite.sharepointonline.com)page into web browser control.
I am using ADFS to get authenticated FedAuth cookie.
How do I pass these cookies to load post login page into browser control.
Thanks for the help.
Hi Vanibasu,
You mean you have the cookie and would like to pass that cookie to your web browser control post login page? If so, this is what i would try:
1. Attach navigate event to the web browser control and verify the page that is loaded. If the loaded page is not the login page, then please try the following:
webBrowser1.Document.Cookie = yourCookieFromADFS;
Thanks,
Hemant.
Hi Vanibasu,
Check this link: http://www.twobitcoder.com/?p=256
Thanks,
Hemant.
Post a Comment