Add to My Yahoo! | Google Reader or Homepage | Add to Windows Live | Add to Windows Live Alerts

Wictor Wilén

Microsoft Certified Master (MCM) - SharePoint 2010 | Microsoft Most Valuable Professional (MVP) - SharePoint Server MVP | Author

How to do active authentication to Office 365 and SharePoint Online

Posted at 2011-05-26 12:54 by Wictor Wilén in SharePoint 2010 , Office 365 with 40 comments.

This is a post detailing how you perform active authentication to SharePoint Online in Office 365. Active authentication is required when you need to authenticate in code to programmatically access SharePoint objects, using for instance Client Object Model, web services or WebDAV from outside of Office 365. When you are "in" SharePoint Online or using the web browser this is not needed since you are either already authenticated and the web browser handles the authentication using active authentication.

Note: The active authentication "mechanism" have unfortunately changed a few times the last month without any notice. I had a really bad timing with one of these changes just a couple of days before demoing it on TechDays here in Sweden. With that said - I cannot guarantee that this method will work in the future. But if it changes I'll try to update the post or write a new one...

SharePoint Online active AuthN basics

Before digging into the actual code I think it is important to understand how it actually works and what the code does. This is easier done using a diagram.

Passive claims AuthN

What happens is that we need to request a token from the STS. In Office 365 the STS is located at https://login.microsoftonline.com/extSTS.srf. To request the token from the STS we pass the username and password using the SAML 1.1 protocol. If the authentication is successful the STS returns a security token. This security token is sent to SharePoint and if SharePoint successfully validates the token it will return two cookies (called FedAuth and rtFa). These two tokens must then be passed along with all requests to SharePoint.

There are some other interesting things happening here  that you need to be aware of. For instance; you need to be aware of which Office 365 subscription you are targeting. P-subscriptions must use HTTP Url's when communicating and E-subscriptions must use HTTPS. Using HTTPS for P-subscriptions will create redirect responses that eventually will drive you crazy when trying to code around them (I got a solution for that though - but I can't get any worse anyways).

How to use Client Object Model with Office 365 from a remote client

To be able to remotely invoke methods on SharePoint Online using Client Object Model (CSOM), web services or WebDAV we need to authenticate first, according to above. Then we need to pass along the cookies for each request. And this is how we do it. Once you have the cookies (FedAuth and rtFA) you need to create a CookieContainer object in which you add the cookies. This CookieContainer must then be added to the request done by the Client Object Model before the request is done. The client runtime Context object has an event called ExecutingWebRequest that can be used for this. The code could look something like this:

context.ExecutingWebRequest += (s,e) => {
    e.WebRequestExecutor.WebRequest.CookieContainer = 
        createCookieContainer();
    e.WebRequestExecutor.WebRequest.UserAgent = userAgent;
};

The createCookieContainer() method is the one responsible for creating the cookie container, more on this one later. Also note here that I set the UserAgent of the request to a new value. This is important! If you do not set any user agent of this request SharePoint Online will gently throw a 403 Forbidden error if you're on an E-subscription. It works fine without on P-subscriptions, but it doesn't harm to add it. So just do it all the time, for the sake of it! The user agent could be any normal browser - this is what I use:

"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)" 

That's basically it! Use the same procedure when you're manually using WebRequest objects or when you're using the SharePoint web services. Just add the cookies and user agent and you're fine.

Show me the code to get the cookies!

Now to the core of this article. How does the code look like to get the actual cookies? As a good TV-chef I've prepared all the things you need to make it really easy for you. I've been using a number of helper classes for a couple of months now and first showed them during TechDays 2011. Chris Johnson, Microsoft, also made a version of them for his blog post on the topic. My helper class has an origin in posts from Steve "SharePoint Claims" Peschka. I've modified and tweaked his code samples so that they work with SharePoint Online.

What I've done is a helper class called MsOnlineClaimsHelper. This class contains all you need to authenticate, retrieve and cache the cookies and piggyback the cookie container on the CSOM web requests. Let's see a very simple sample:

MsOnlineClaimsHelper claimsHelper = new MsOnlineClaimsHelper(url, username, password);

using (ClientContext context = new ClientContext(url)) {

    context.ExecutingWebRequest += claimsHelper.clientContext_ExecutingWebRequest;

    context.Load(context.Web);
                
    context.ExecuteQuery();

    Console.WriteLine("Name of the web is: " + context.Web.Title);
                
}

On the first line I create the helper object and pass in the URL, username and password. This class will once used do the active authentication for you and cache the cookies until they expire. It will handle the HTTP/HTTPS problem with the E/P-subscriptions mentioned earlier, the User Agent problem and everything else you need. Yes, you will be able to download the code later. After creating the client context I hook up a helper method of the MsOnlineClaimsHelper class called clientContext_ExecutingWebRequest. This method is the one adding the cookies and fixing the user agent. Then it's just to use the client object model as usual. Remember that when you are using P-subscriptions the URL's passed into the client object model must be HTTP (the helper class doesn't really care and can handle both) and use HTTPS for E-subscriptions.

The helper class is made so that you can reuse it, so you don't have to re-authenticate all the time, since that will really slow your application down. If you need the CookieContainer to add to your own web requests it has a property called (surpise!) CookieContainer that you can use.

To illustrate another use, that also is a very useful helper class, is a Office 365 claims aware WebClient derivative.

public class ClaimsWebClient : WebClient {
    private readonly MsOnlineClaimsHelper claimsHelper;

    public ClaimsWebClient(Uri host, string username, string password) {
        claimsHelper = new MsOnlineClaimsHelper(host, username, password);
    }
    protected override WebRequest GetWebRequest(Uri address) {
        WebRequest request = base.GetWebRequest(address);
        if (request is HttpWebRequest) {
            (request as HttpWebRequest).CookieContainer = claimsHelper.CookieContainer;
        }
        return request;
    }
}

This is a class that can be used just as the normal WebClient class. Using this one you enable WebDAV fiddling with SharePoint Online. It's very useful to upload and retrieve documents using PUT and GET methods.

The download

I promised you to see the code how we actually retrieves the cookies. Sorry, I won't. I'll leave that to you. Download the code sample, including all helper classes, by clicking this link and experiment with it as you like.

Happy coding!

Updated: Sometimes you're tired - mixed up passive and active...

Comments and trackbacks

#  You rock! by Phill Duffy
Screenshot from websnpr Hi Wictor, been pulling my hair out for a couple of days on Chris Johnsons code for this, I am glad you have got this update code out there and I have tested it successfully! Thanks again Phill
#  @Phil by Wictor
Screenshot from websnpr Yea, CJ's code worked perfectly just a couple of days ago :). But this is the nature of a beta. And as I wrote in the post, they have changed it a couple of times already so I guess they have more changes in the pipe.
#  the update CJ code is good to use by laptop adapter wholesaler
Screenshot from websnpr yes,i also try to use the update Chris Johnsons code,it really tested well,i love it,thank you very much
#  Great Job by mincho
Screenshot from websnpr Hi Wictor, great job! Could you provide a sample how to access SP-Online via web-service. CSOM works perfectly.
#  It works by Arthur
Screenshot from websnpr Thanks Wictor, the UserAgent trick solved my problem. But Office 365 seems to be rejecting HTTP PUT method when I am using Microsoft.SharePoint.Client.File.SaveBinaryDirect() to create a new file on 'Shared Documents'. Do you have any idea why this is happening? Or any other suggestion to perform the same task? Thank you.
#  @Arthur by Wictor
Screenshot from websnpr Haven't tried SaveBinaryDirect. I have used the ClaimsWebClient (included in the sample) with the PUT method to upload stuff into SPOL.
#  SaveBinaryDirect fire event too late by Arthur
Screenshot from websnpr Thanks again, I got it to work with my own HTTP request. SaveBinaryDirect() fires ExecutingWebRequest event after the request stream is flushed and closed. By then it is too late to add the cookies to the request. Cheers :)
#  Does not support windows xp by Chris
Screenshot from websnpr Thanks, but any idea as to how I can have it working on Windows XP as it does not support Microsoft.IdentityModel.dll and specifically the WSTrustChannel.
#  Can we authenticate 365 site using webservice(.asmx)? by Ashish
Screenshot from websnpr Hi, I would like to authenticate user for 365 using webservices in my window application. Can you please provide comments how can I achieve this things? Thanks, Ashish Chotalia
#  @Chris by Wictor
Screenshot from websnpr Since the WIF isn't supported on XP I think your only option is to use Reflector and build the stuff yourself. It's essentially HTTP and encryption.
#  Screen Scraped Authentication to Office 365 and SharePoint Online by Trackback
Screenshot from websnpr Body: Way back in April, Microsoft published a very good article by Robert Bogue entitled Remote Authentication
#  Screen Scraped Authentication to Office 365 and SharePoint Online by Trackback
Screenshot from websnpr Body: Way back in April, Microsoft published a very good article by Robert Bogue entitled Remote Authentication
#  Cannot contact site by Jamie
Screenshot from websnpr Hi, When I try your code (and CJs), I get the error Microsoft.SharePoint.Client.ClientRequestException: Cannot contact site at the specified URL https://thickshake.sharepoint.com/ I'm incredibly lost here. I can access the site in my browser, do I need to allow certain permissions for this to happen?
#  SSO / Active Directory by Christine
Screenshot from websnpr Any suggestions on how to get the token for a SharePoint Online site using SSO through Active directory synchronization? Thanks!
#  How to connect to ExcelService in SharePoint online? by Ronak
Screenshot from websnpr I tried to add service reference to ExcelService.asmx but the proxy created does not work with SharePoint online.
#  How to upload a file? by Berend Engelbrecht
Screenshot from websnpr Hi, I am trying to programmatically access files on office 365 sharepoint server. Your article helped me a long way: authentication works fine and I can download files without problems. But Client.File.SaveBinaryDirect gives me 405 "method not allowed". It would be great if you could explain how to upload. Thank you!
#  Error by Kaustubh
Hi, Thanks for this awesome code, however am gatting the following error when trying to run your code through a console application: An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. InnerException: Authentication Failure This happens on the following method: public Message EndIssue(IAsyncResult asyncResult) under public partial class WSTrustFeb2005ContractClient : ClientBase, IWSTrustFeb2005Contract in WcfClientContracts.cs. Could you please suggest what might be causing this and suggest a solution? I've confirmed that the credentials I pass are correct. I'm trying to hit the following url: https://microsoft.sharepoint.com/sites/LCA/iea5ppe
#  Alternative to SaveBinaryDirect by SEvans
Screenshot from websnpr Great piece of code Wichtor. As others have noted, SaveBinaryDirect does not work correctly, as the FedAuth cookies never get attached to the HTTP PUT request that the method generates. Here's my workaround - hope this helps some of you: // "url" is the full destination path (including filename, i.e. https://mysite.sharepoint.com/Documents/Test.txt) // "cookie" is the CookieContainer generated from Wichtor's code // "data" is the byte array containing the files contents (used a FileStream to load) System.Net.ServicePointManager.Expect100Continue = false; HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest; request.Method = "PUT"; request.Accept = "*/*"; request.ContentType = "multipart/form-data; charset=utf-8"; request.CookieContainer = cookie; request.AllowAutoRedirect = false; request.UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"; request.Headers.Add("Accept-Language", "en-us"); request.Headers.Add("Translate", "F"); request.Headers.Add("Cache-Control", "no-cache"); request.ContentLength = data.Length; using (Stream req = request.GetRequestStream()) { req.Write(data, 0, data.Length); } HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream res = response.GetResponseStream(); StreamReader rdr = new StreamReader(res); string rawResponse = rdr.ReadToEnd();
#  Alternative to SaveBinaryDirect by SEvans
Screenshot from websnpr Great piece of code Wichtor. As others have noted, SaveBinaryDirect does not work correctly, as the FedAuth cookies never get attached to the HTTP PUT request that the method generates. Here's my workaround - hope this helps some of you: // "url" is the full destination path (including filename, i.e. https://mysite.sharepoint.com/Documents/Test.txt) // "cookie" is the CookieContainer generated from Wichtor's code // "data" is the byte array containing the files contents (used a FileStream to load) System.Net.ServicePointManager.Expect100Continue = false; HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest; request.Method = "PUT"; request.Accept = "*/*"; request.ContentType = "multipart/form-data; charset=utf-8"; request.CookieContainer = cookie; request.AllowAutoRedirect = false; request.UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"; request.Headers.Add("Accept-Language", "en-us"); request.Headers.Add("Translate", "F"); request.Headers.Add("Cache-Control", "no-cache"); request.ContentLength = data.Length; using (Stream req = request.GetRequestStream()) { req.Write(data, 0, data.Length); } HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream res = response.GetResponseStream(); StreamReader rdr = new StreamReader(res); string rawResponse = rdr.ReadToEnd();
#  Alternative to SaveBinaryDirect by SEvans
Screenshot from websnpr Great piece of code Wichtor. As others have noted, SaveBinaryDirect does not work correctly, as the FedAuth cookies never get attached to the HTTP PUT request that the method generates. Here's my workaround - hope this helps some of you: // "url" is the full destination path (including filename, i.e. https://mysite.sharepoint.com/Documents/Test.txt) // "cookie" is the CookieContainer generated from Wichtor's code // "data" is the byte array containing the files contents (used a FileStream to load) System.Net.ServicePointManager.Expect100Continue = false; HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest; request.Method = "PUT"; request.Accept = "*/*"; request.ContentType = "multipart/form-data; charset=utf-8"; request.CookieContainer = cookie; request.AllowAutoRedirect = false; request.UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"; request.Headers.Add("Accept-Language", "en-us"); request.Headers.Add("Translate", "F"); request.Headers.Add("Cache-Control", "no-cache"); request.ContentLength = data.Length; using (Stream req = request.GetRequestStream()) { req.Write(data, 0, data.Length); } HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream res = response.GetResponseStream(); StreamReader rdr = new StreamReader(res); string rawResponse = rdr.ReadToEnd();
#  Modelos de integración remota con SharePoint Online by Trackback
Screenshot from websnpr Ya hemos adquirido Office365 y tenemos que subir a SharePoint Online los documentos que ya tenemos en
#  SPC11: Out of the Sandbox and into the Cloud by Trackback
Screenshot from websnpr Last week I was at the SharePoint 2011 show in Anaheim. My session focus during this show was to get
#  Won't work with P1 public web site with custom domain by Dave
Screenshot from websnpr When using P1 plan and set SharePoint public web site to use custom site such as "www.company.com", the team site url changed too. This will break the authentication using the code described here.
#  THIS ARTICLE RULES! by jason
Screenshot from websnpr THANKS FOR PUTTING THIS TOGETHER IN ONE PLACE!! awesome work!
#  I am using this in an OS Project by Henning Groß
Screenshot from websnpr Hi! I am using your classes without modifications and with credits to this site in this open source project: https://evolvis.org/projects/dmsapi/ I hope this is OK but had a hard time figuring what the license on your code is. Please contact me if you dont like your code to be included and I will write my own implementation removing yours. I will spend a few more working days on implementing features and tests and would love to have more committers to make this api even more powerful. Anyone who wants their application to be able to communicate with different versions of sharepoint or other DMS-Systems even feel free to use/improve it.
#  Fantastic Post - Live saver by Fabian Williams
Screenshot from websnpr Dude, next time i see you agian, Beers are on Me! Now Im gonna try and extend it :-)
#  WebException by Henning Groß
Screenshot from websnpr Hi! I am getting: WebException: The underlying connection was closed from time to time. Any idea what could cause that? Id appreciate a solution :)
#  WebException / Connection Reset by Henning
Screenshot from websnpr Hi! If anyone experiences the same problems. It seems like this fixes it: protected override WebRequest GetWebRequest(Uri address) { ... (request as HttpWebRequest).KeepAlive = true; ... } AND public void clientContext_ExecutingWebRequest(object sender, WebRequestEventArgs e) { ... e.WebRequestExecutor.RequestKeepAlive = true; } I dunno if both are nessecary.
#  Great work Wictor!!! by Niclas Pålsson
Screenshot from websnpr I got a call from one of our contacts at Microsoft, they asked if we support Office SharePoint 365 in our Document Management system (DS2011) for Dynamics CRM. It didn't work out of the box but after reading your post we solved the authentication issues and can now add Office SharePoint 365 to the list of SharePoint scenarios supported, and this just a few days after Microsoft released the support for integration between Office 365 and CRM online. Sweet! Thanks for a great post!
#  SharePoint Online: Como usar los servicios web expuestos! by Trackback
Screenshot from websnpr Aunque a la hora de trabajar de forma remota con SharePoint Online en Office 365 la recomendación
#  Cómo utilizar los servicios web expuestos en SharePoint Online by Trackback
Screenshot from websnpr Aunque a la hora de trabajar de forma remota con SharePoint Online en Office 365 la recomendación
#  Federation Service by Thomas Jorgensen
Screenshot from websnpr Wonderfully helpful - at least as long as you stay clear of federated users. Unfortunately, I presently need to authenticate against an Office365 with federation service enabled, and that stops this otherwise great solution short. Using the Gui, once the user name has been entered, the user is asked to login to 'fed'. As long as I am using the GUI, this works fine. Using your code, I run into the following exception: Object reference not set to an instance of an object. Stack trace: at Wictor.Office365.MsOnlineClaimsHelper.getCookieContainer() in D:\Projects\A2\MigrateTool\Wictor.Office365.ClaimsDemo\MsOnlineClaimsHelper.cs:line 79 at Wictor.Office365.MsOnlineClaimsHelper.get_CookieContainer() in D:\Projects\A2\MigrateTool\Wictor.Office365.ClaimsDemo\MsOnlineClaimsHelper.cs:line 118 ... I have debugged the exception to the following lines - specifically it occurs in the EndIssue method: Message response = trustClient.EndIssue( trustClient.BeginIssue( Message.CreateMessage( MessageVersion.Default, WSTrustFeb2005Constants.Actions.Issue, new RequestBodyWriter(trustSerializer, rst) ), null, null)); Any idea how to fix this? I have been searching far and wide for code to authenticate against federated users in Office365, but so far without luck.
#  Connection Failure by Kirk Liemohn
Screenshot from websnpr I am getting a connection failure. This blog is over 7 months old, so I'm wondering if things have changed since then or if the problem is more on my end. I get the following error with no additional information (no inner exception) when I do context.ExecuteQuery(): Cannot contact site at the specified URL http://threewill.sharepoint.com/sites/kirk. I haven't dug in too far yet, but I thought I would post this before spending too much time in case I'm not the only one.
#  Optimization + SSO federation support by Rodion
Screenshot from websnpr I think the code is over-complicated in a part where you get the token from STS. If you are using the WIF then there is alredy a UserNameWSTrustBinding and WsTrustChannel implemented. I'm not sure if I can post the code here but it is half of the size and no need for serialization / XML tricks. Apart from that as it was already mentioned in several comments above, it would be geat to see the way to authenticate using SSO credentials. It looks like extSTS.srf does not accept tokens issued by trusted federated authority. At least using standard WsTrust methods. Any suggestions? (apart from using passive federation via login.sfr)
#  username/password by Dan
Screenshot from websnpr This is a great post. Through a Silverlight client running in a WebPart, I am already authenticated on SPO through the browser and I'd rather not prompt the user for a username/password in order to get the token. Through Silverlight's client HTTP stack, there is support for cookies but I can't just grab the one that is in the browser (I don't think), and I can't use the browser HTTP stack because I need to do a "PUT" in order to stream up a large file. Can you think of a solution?
#  redirect to yoursite.sharepoint.com by al
Screenshot from websnpr Thank you for your post. Can I use this approach to authenticate against the team site? like by redirecting along with passing the cookies to the www.yoursite.sharepoint.com ?
#  what will be url for trial version of office365? by Sagar
Thank you for your post. I want to know, what will be url for trial version of office365. currently i m providing it as "https://mydomainname.onmicrosoft.sharepoint.com" but it not returning cookies. i have provided right credential. trail plan is E3. thats why i have use https instead of http. help me.
#  saml token does not contain binarysecuritytoken by TheGrimAce
I tried the code, but when I get the SAML token from the site in getSamlToken(), I don't have a BynarySecurityToken in it. Any ideas what could be the problem?
#  saml token does not contain binarysecuritytoken by TheGrimAce
I tried the code, but when I get the SAML token from the site in getSamlToken(), I don't have a BynarySecurityToken in it. Any ideas what could be the problem?
#  same error with EndIssue call! by Raj PArmar
Screenshot from websnpr Thomas Jorgensen has mentioned issue with EndIssue call..I am also getting same error.Any help? Thx. CALL >> return base.Channel.EndIssue(asyncResult);
Make a comment on this post:
Subject:  

Your name:  
Your Url:  
Note: submissions may have to be approved before being visible, so don't submit your comment multiple times.