Final Installment: Office 365, Azure, and Authentication

The blog post is the final installment in a series on Office365, Windows Azure, and Authentication, featuring an O365 authorization helper library.

This post is part of a series on Office365/SharePoint Online, Windows Azure and Authentication. The other posts in this series can be found here: Office365/SharePoint Online, Windows Azure and Authentication

In this last post in my Office365/SharePoint Online + Windows Azure + Authorization blog series, I want to introduce a little helper project I am using. To make life easier I created a little O365 authorization helper library that does a lot of the heavy lifting for you. It covers two of the three things I outlined in my series as workarounds.

Code samples I show in this post were taken from a code sample CPT’s Office365/SharePoint Online Claims Authentication Helper Library… you can get the code from the Critical Path Training site’s Members section**…** look in the Code Samples section.

Introducing the Claims Auth Friendly ClientContext: ClaimsClientContext

First it creates a special Client Site Object Model (CSOM) ClientContext object. This object has a few properties needed for authenticating with Microsoft Online (MSO) to obtain the SAML token. It then rewrites the ClientContext so that every request includes the SAML token:

namespace CriticalPathTraining.Office365.AuthLibrary {
  public class ClaimsClientContext : ClientContext {
    public string MsoUsername { get; set; }
    public string MsoPassword { get; set; }
    public string MsoRootSiteCollectionUrl { get; set; }

    public ClaimsClientContext(string webFullUrl) : base(webFullUrl) { }
    public ClaimsClientContext(Uri webFullUrl) : base(webFullUrl) { }

    private MsOnlineClaimsHelper _claimsHelper;

    ///
    /// Microsoft Online claims helper used to authenticate to SharePoint Online.
    ///
    private MsOnlineClaimsHelper MsftOnlineClaimsHelper {
      get {
        if (_claimsHelper == null) {
          _claimsHelper = new MsOnlineClaimsHelper(MsoUsername,
                                                   MsoPassword,
                                                   MsoRootSiteCollectionUrl);
        }
        return _claimsHelper;
      }
    }

    ///
    /// Rewire event for client context so that every request includes
    /// authenticated cookies.
    ///
    protected override void OnExecutingWebRequest(WebRequestEventArgs args) {
        args.WebRequestExecutor.WebRequest.CookieContainer
            = MsftOnlineClaimsHelper.CookieContainer;
    }
  }
}

Usage is very simple… the downloadable library includes a test project that shows the usage:

[TestMethod]
public void CliamsClientContextTest() {
  using (var context = new ClaimsClientContext(MSO_SPSITE_URL) {
    MsoUsername = MSO_USERNAME,
    MsoPassword = MSO_PASSWORD,
    MsoRootSiteCollectionUrl = MSO_ROOT_SPSITE_URL
  }) {
    // get the web
    var web = context.Web;
    context.Load(web, w => w.Title);
    context.ExecuteQuery();

    Assert.IsNotNull(web.Title);
    Assert.IsTrue(web.Title.Length > 0);

    Console.WriteLine("Retrieved site title:" + web.Title);
  }
}

Introducing the Claims Friendly Web Client: ClaimsWebClient

The other thing I give you is a special version of the WebClient class that’s makes working with claims a bit easier. It has a single property where you specify the CookieContainer that will contain the SAML token. The library exposes the samples Wictor Wilen provided on to do the authentication for you and generate the CookieContainer:

namespace CriticalPathTraining.Office365.AuthLibrary {
  public class ClaimsWebClient : WebClient {
    ///
    /// Cookies that should be included on every Web request.
    ///
    public CookieContainer CookieContainer { get; set; }

    ///
    /// Override base GetWebRequest() method to always include cookies if
    /// they were specified.
    ///
    protected override WebRequest GetWebRequest(Uri address) {
      var request = base.GetWebRequest(address);

      if (request is HttpWebRequest && CookieContainer != null)
        ((HttpWebRequest)request).CookieContainer = CookieContainer;

      return request;
    }
  }
}

In the associated test project you’ll also find the usage for this as well:

[TestMethod]
public void ClaimsWebClientTest() {
  // file to download
  string fileToDownload = "/_layouts/images/siteIcon.png";

  var claimsHelper = new MsOnlineClaimsHelper(MSO_USERNAME,
                                              MSO_PASSWORD,
                                              MSO_ROOT_SPSITE_URL);
  using (var webClient = new ClaimsWebClient() {
    CookieContainer = claimsHelper.CookieContainer
  }) {
    // get the file
    var fileStream = ((ClaimsWebClient)webClient).OpenRead(
        string.Format("{0}{1}", MSO_SPSITE_URL, fileToDownload)
    );

    // download & write local
    string tempFilePath = Path.GetTempFileName();
    var tempFile = File.Open(tempFilePath, FileMode.OpenOrCreate);
    fileStream.CopyTo(tempFile);
    fileStream.Close();
    tempFile.Close();

    Console.WriteLine("Downloaded file to:" + tempFilePath);

    // make sure file exists & bigger than 0 bytes
    Assert.IsTrue(File.Exists(tempFilePath));
    var fileInfo = new FileInfo(tempFilePath);
    Assert.IsTrue(fileInfo.Length > 0);
  }
}

Last but not least, for completeness I threw in a test for working with any of the SharePoint *.ASMX or *.SVC Web services. You don’t need any special helpers here as they include a CookieContainer class already:

[TestMethod]
public void WebServiceTest() {
  XmlNode results;

  var claimsHelper = new MsOnlineClaimsHelper(MSO_USERNAME,
                                              MSO_PASSWORD,
                                              MSO_ROOT_SPSITE_URL);
  using (var client = new Lists() {
    Url = string.Format("{0}_vti_bin/Lists.asmx", MSO_SPSITE_URL),
    UseDefaultCredentials=true,
    CookieContainer = claimsHelper.CookieContainer
  }) {
    results = client.GetList("Shared Documents");
  }

  Assert.IsNotNull(results);
}
Andrew Connell
Developer & Chief Course Artisan, Voitanos LLC. | Microsoft MVP
Written by Andrew Connell

Andrew Connell is a full stack developer who focuses on Microsoft Azure & Microsoft 365. He’s a 20+ year recipient of Microsoft’s MVP award and has helped thousands of developers through the various courses he’s authored & taught. Andrew’s mission is to help web developers become experts in the Microsoft 365 ecosystem, so they can become irreplaceable in their organization.

Share & Comment