SharePoint Online Authentication without SharePoint API

February 5, 2015 at 12:24 pm 6 comments

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.

authprocess


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();
office365auth01

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);
office365auth02

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();
office365auth03

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;
}
}
office365auth04

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();
office365auth05


Advertisements

Entry filed under: SharePoint Development.

SharePoint for the Social Enterprise

6 Comments Add your own

  • 1. Duncan Hepple  |  February 19, 2015 at 10:41 am

    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

    Reply
  • 2. erwinkoens  |  February 24, 2015 at 10:46 am

    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

    Reply
  • 3. Diogo Xavier Luís  |  December 2, 2015 at 8:51 am

    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”?

    Reply
    • 4. erwinkoens  |  December 2, 2015 at 9:56 am

      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!

      Reply
      • 5. Diogo Xavier Luís  |  December 2, 2015 at 10:38 am

        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 😦

      • 6. SiobhanB (@SiobhanBaynes)  |  July 6, 2016 at 3:26 pm

        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:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

Trackback this post  |  Subscribe to the comments via RSS Feed


Recent Posts

Twitter Updates

Error: Twitter did not respond. Please wait a few minutes and refresh this page.


%d bloggers like this: