Accessing SharePoint Online with the SharePoint API is quite easy. You just have to create a SharePoint client context using the SharePoint Client assembly.
Suppose you cannot reference this SharePoint Client assembly. For example if you want to create a CRM Online Plug-In. These plug-ins live in a sandboxed environment where no external assemblies are allowed. How can you access SharePoint Online? There are several blog post about this topic, but the examples didn’t work for me (Luc Stakenborg and Wictor Wilén).
Based on these blog post, my dear colleagues Rene Brauwers and Wesley Bakker and logging in 1000 times at my demo tenant and analyzing the Fiddler logs I came to this solution.
You can view the code at GitHub.
Step 1 – Requesting a RpsContextCookie
string requiredAuthUrl = string.Format("{0}/_layouts/15/Authenticate.aspx?Source={1}", endPoint, HttpUtility.UrlDecode(endPoint));
HttpWebRequest request0 = (HttpWebRequest)WebRequest.Create(requiredAuthUrl);
request0.Accept = "*/*";
request0.Headers.Set(HttpRequestHeader.AcceptLanguage, "en-US");
request0.Headers.Set(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
request0.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3)";
request0.AllowAutoRedirect = false;
request0.CookieContainer = cookies;
var response0 = (HttpWebResponse)request0.GetResponse();
Step 2 – Send SAML request and receive Binairy Security Token.
var request1 = (HttpWebRequest)WebRequest.Create(stsEndpoint);
request1.CookieContainer = cookies;
var data = Encoding.ASCII.GetBytes(payLoadSts);
request1.Method = "POST";
request1.ContentType = "application/xml";
request1.ContentLength = data.Length;
using (var stream1 = request1.GetRequestStream())
{
stream1.Write(data, 0, data.Length);
}
// Response 1
var response1 = (HttpWebResponse)request1.GetResponse();
var responseString = new StreamReader(response1.GetResponseStream()).ReadToEnd();
// Get BinarySecurityToken
var xData = XDocument.Parse(responseString);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("S", "http://www.w3.org/2003/05/soap-envelope");
namespaceManager.AddNamespace("wst", "http://schemas.xmlsoap.org/ws/2005/02/trust");
namespaceManager.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
var BinarySecurityToken = xData.XPathSelectElement("/S:Envelope/S:Body/wst:RequestSecurityTokenResponse/wst:RequestedSecurityToken/wsse:BinarySecurityToken", namespaceManager);
Step 3 – Send Security Token and receive Autehtication Cookies (rtFA & FedAuth)
var request2 = (HttpWebRequest)WebRequest.Create(signInUrl);
var data2 = Encoding.ASCII.GetBytes(BinarySecurityToken.Value);
request2.Method = "POST";
request2.Accept = "*/*";
request2.Headers.Set(HttpRequestHeader.AcceptLanguage, "en-US");
request2.ContentType = "application/x-www-form-urlencoded";
request2.UserAgent = browserUserAgent;
request2.Headers.Set(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
request2.Host = browserHost;
request2.ContentLength = data2.Length;
request2.CookieContainer = cookies;
using (var stream2 = request2.GetRequestStream())
{
stream2.Write(data2, 0, data2.Length);
}
// Response 2
var response2 = (HttpWebResponse)request2.GetResponse();
var responseString2 = new StreamReader(response2.GetResponseStream()).ReadToEnd();
Step 4 – Request and receive Form Digest
string restUrl3 = string.Format("{0}/_api/contextinfo", endPoint);
var request3 = (HttpWebRequest)WebRequest.Create(restUrl3);
request3.CookieContainer = cookies;
request3.Method = "POST";
request3.ContentLength = 0;
// Response 3
string formDigest = string.Empty;
using (var response3 = (HttpWebResponse)request3.GetResponse())
{
using (var reader = new StreamReader(response3.GetResponseStream()))
{
var result = reader.ReadToEnd();
// parse the ContextInfo response
var resultXml = XDocument.Parse(result);
// get the form digest value
var x = from y in resultXml.Descendants()
where y.Name == XName.Get("FormDigestValue", "http://schemas.microsoft.com/ado/2007/08/dataservices")
select y;
formDigest = x.First().Value;
}
}
Step 5 – Action : Create a Site!
string restUrl4 = string.Format("{0}/_api/web/webinfos/add", endPoint);
var request4 = (HttpWebRequest)WebRequest.Create(restUrl4);
request4.CookieContainer = cookies;
request4.Method = "POST";
request4.Accept = "application/json; odata=verbose";
request4.ContentType = "application/json;odata=verbose";
request4.Headers.Add("X-RequestDigest", formDigest);
string json4 = " {'parameters': { " +
"'__metadata': {'type': 'SP.WebInfoCreationInformation' }, " +
"'Url': 'RestSubWeb', " +
"'Title': 'RestSubWeb', " +
"'Description': 'REST created web', " +
"'Language':1033, " +
"'WebTemplate':'sts', " +
"'UseUniquePermissions':false} } ";
var data4 = Encoding.ASCII.GetBytes(json4);
request4.ContentLength = data4.Length;
using (var stream4 = request4.GetRequestStream())
{
stream4.Write(data4, 0, data4.Length);
}
// Response 4
var response4 = (HttpWebResponse)request4.GetResponse();
var responseString4 = new StreamReader(response4.GetResponseStream()).ReadToEnd();
Hi there,
I’m struggling with getting this working with ADFS as the IP-STS, as we use federated identities. Whenever I send a SAML request token I just get a response containing an X509 certificate and not a binary security token.
Also, this is the first time that I’ve seen the RpsContextCookie being required for login – is this a new requirement?
Thanks
Duncan
Hi Duncan, don’t know if the RpsContextCookie is a new requirement, but it works for me. If I didn’t get that cookie the authentication fails. Do you write the same headers? That was also a challenge that I had. Please e-mail me if you do not succeed.
Cheers, Erwin
Hi there,
I’m using this approach to authenticate against SharePoint Online OData, as I’m having trouble with it when the authentication is a hybrid setup.
How do you define “extStsTemplate.xml”?
Hi Diogo, I’ve uploaded my version of ‘extStsTemplate.xml’ to Git. https://github.com/runmotion/SharePointOnlineAuthentication
Got this file by analyzing the default process of authentication. Good luck!
Thank you so much for the speedy reply. I ended up getting the same file via fiddler too 🙂 But it’s nice to get a confirmation.
Can you give me examples of all your parameters (ie endPoint, requestUrl, stsEndpoint, signInUrl and browserHost).
I’m still getting 403 on request3, and I’m not sure what I’m doing wrong 😦
Thanks for this, nice code. It works fine for me without the RpsContextCookie though, I can remove lines 40-52 quite happily.
For anyone else reading this, these were my config entries:
Hello, I can get Token and FedAuth and rtFa cookie .
But I want to use those data to login my sharepoint site page(https://teaner.sharepoint.com/site/mysite)on browser.
How to do it?
Thank you.
Hi Duncan,
The code builds successfully as is, but it would be nice to have a detailed explanation on how to properly configure the App.Config file, specifically for:
SharePointOnlineEndpoint
SharePointOnlineSTS
SharePointOnlineSignInUri
BrowserHost
BrowserUserAgent
After configuring those, is that all we need to do?
Does the github code automatically create a new site? what if we don’t want to create a new site, all we want to do is authenticate in order to successfully use SSIS SharePoint List Adapter to download/ upload/ update sharepoint online 365 list records. At what part of the code can we start to do other actions when authentication is successful?
Hi Erwin,
The code builds successfully as is, but it would be nice to have a detailed explanation on how to properly configure the App.Config file, specifically for:
SharePointOnlineEndpoint
SharePointOnlineSTS
SharePointOnlineSignInUri
BrowserHost
BrowserUserAgent
After configuring those, is that all we need to do?
Does the github code automatically create a new site? what if we don’t want to create a new site, all we want to do is authenticate in order to successfully use SSIS SharePoint List Adapter to download/ upload/ update sharepoint online 365 list records. At what part of the code can we start to do other actions when authentication is successful?