An API gateway to business productivity

What would you build if you could process the data generated by your business operations in real time? You could, for example:

  • See trending/abandoned documents and usage patterns.
  • Scan calendars to suggest optimum meeting times.
  • Map collaboration points between departments.
  • Automate a change management/approval process.
  • Manage a backlog of work.

And that’s just for starters! I would be willing to bet that a very large slice of business performed in the world today is driven by the Microsoft Office apps, so imagine the potential gains around automating some of that? It’s got to be huge.

The route to building client applications for Office 365 is Graph API, a RESTful HTTP interface that you can use to drive your Office data. But that’s not all, Graph API can also drive:

  • Azure AD
  • OneDrive
  • SharePoint
  • Planner
  • Teams

It should be clear that this is a useful tool to have in your box. Today, I’m going to help you get started on the road to being a productivity hero.

Requirements

This is something of a follow-up to my blog post on Authenticating with Azure AD and OpenID Connect – where I cover how a client application authenticates with Azure AD and retrieves an access token for accessing user data. This is exactly how an app authenticates with Graph API, so that post is required reading to get you started. This post is going to focus on what you do to interact with Graph API after you acquire an access token.

Tool Version used in this post Link Notes
ASP.NET MVC 5.2.3
Microsoft Graph 1.6.2 NuGet
Microsoft Authentication Library (MSAL) 1.1.0-preview NuGet
HtmlAgilityPack 1.5.5 NuGet

Demo Application

The demo I have written to support this post is a simple to do list app written in ASP.NET MVC. The task list is synchronized to a OneNote page owned by the signed in user and any additions, edits or deletions are reflected in both.

The full source listing for the demo is available on GitHub.

The simplest way to understand what it does is to show you some screenshots. First a user signs-in with their Azure AD identity:

sign_in_with_microsoft

Then they can view their task list:

todo_list

They can add new tasks or edit/delete existing ones:

new_todo

todo_list_with_new

Any changes are automatically reflected in OneNote:

onenote_sync

It’s basically a simple CRUD app that synchronizes with OneNote. I thought it was a good choice for demonstrating the key operations of a REST API.

If you’re not interested in OneNote specifically, you will still hopefully get some value from this post. Whether you want Excel data or something else entirely, you will be using the same Graph client library and authentication mechanism that my app demonstrates. The objects you will be using in the client library and their underlying URIs will be different, but not a lot else will be. You’ll still be doing reads, writes, creations and deletes of objects in that client library, as I do.

Using the Microsoft Graph client

In ASP.NET, interactions with Graph API are performed with an object called GraphServiceClient. It exposes an object model that maps over the URI structure of the Graph API, you can perform gets, sets, adds and deletes on these objects to invoke the corresponding REST method over HTTP.

The easiest way to understand the client is to see it in action, so let’s cut to the chase. I’m going to explain one key method in each layer of the application stack, rather than attempt to cover all the code (which would make for a very lengthy blog post). If the REST method you are most interested in is not covered in the breakdown, check the full code listing to see how my demo does it against OneNote.

OneNoteService

At the bottom of the application stack, we have a OneNoteService that takes a GraphServiceClient and performs the synchronization with the user’s OneNote. It doesn’t know anything about to do lists, it only deals with OneNote primitives: notebooks, sections and pages.

Here is one of the methods from that class, the GetPageContent method. It returns a Stream to the HTML content of an existing OneNote page:

public static async Task<System.IO.Stream> GetPageContent(GraphServiceClient client, OnenotePage page, bool includeIds = false)
{
    List<QueryOption> queryOptions = new List<QueryOption>();
    if (includeIds)
        queryOptions.Add(new QueryOption("includeIDs", "true"));

    return await client.Me.Onenote.Pages[page.Id].Content.Request(queryOptions).GetAsync();
}

An example URI for this sort of resource is https://graph.microsoft.com/v1.0/users/48d31887-5fad-4d73-a9f5-3c356e68a038/onenote/pages/1-6f4652b0d1cf455482daca8c9108fc08!1-0bc35248-e4e2-4759-ad85-89407bceccfe/content?includeIDs=true. It’s a bit of a mouthful.

Take a look at the above return statement again. It’s getting the resource using an object model and that object model has a similar structure to the lengthy URI.

  • Me appears to map over users/48d31887-5fad-4d73-a9f5-3c356e68a038.
  • OneNote.Pages[pageId] looks a little like a representation of onenote/pages/1-6f4652b0d1cf455482daca8c9108fc08!1-0bc35248-e4e2-4759-ad85-89407bceccfe. It’s just representing the page resources in a hashtable that we can index into.
  • Content.Request(queryOptions) seems to get transformed to content?includeIDs=true.

The GraphServiceClient translates the use of the object model into the URI of a specific resource. The GetAsync() method performs an HTTP GET request against the real URI, parses the response into the appropriate object and returns that object representation. You can see why using the object model is appealing, it’s a lot more convenient than trying to track those URIs (containing all those GUIDs) and performing the object mapping yourself.

This process is actually a standard protocol, called OData. GraphServiceClient is an OData client and all that Graph API offers can be accessed through its data model, not just the objects related to OneNote that I’ve chosen for my example.

ToDoService

The next layer up is the ToDoService, which wraps the OneNoteService and defines the key operations for synchronizing a user’s to-do list. It is also responsible for authenticating the GraphServiceClient, but more on that later.

Here is a sample method that uses the GetPageContent method I described above:

public async Task<List<ToDo>> GetToDoList()
{
    // First get the synchronized page. If it doesn't exist, create it.
    var client = GraphServiceHelper.GetAuthenticatedClient();
    var pages = await OneNoteService.GetMyPages(client);
    var todoPage = pages.Where(page => page.Title == todoPageTitle).SingleOrDefault();

    if (todoPage == null)
        todoPage = await CreateToDoPage(client);

    // Read the HTML content of the synchronized page and translate that into ToDo objects.
    List<ToDo> result = new List<ToDo>();
    Stream contentStream = await OneNoteService.GetPageContent(client, todoPage);
    using (StreamReader reader = new StreamReader(contentStream))
    {
        string pageContent = reader.ReadToEnd();
        var document = new HtmlDocument();
        document.LoadHtml(pageContent);

        // A OneNote todo list is a sequence of tags with the data-tag attribute set
        // to "to-do" or "to-do:completed".
        foreach (var node in document.DocumentNode.Descendants("p").Where(
            p => p.GetAttributeValue("data-tag", "not-to-do") != "not-to-do"))
        {
            string dataTag = node.Attributes["data-tag"].Value;
            if (dataTag != "to-do" && dataTag != "to-do:completed")
                continue; // node is not a OneNote todo

            result.Add(new ToDo
            {
                Task = node.InnerText,
                Done = dataTag == "to-do:completed"
            });
        }
    }

    return result;
}

This method:

  • Lazily creates the to-do list OneNote page, if it needs to.
  • Reads the HTML content of the page.
  • Translates any OneNote task (an entry with a checkbox, as seen in the list of screenshots above) into the list of to-dos that make up our page view.

The code to process the synchronized to-do list is a lot easier to understand if you can see the kind of HTML we need to process:

todo_html

A OneNote to-do is a p tag with a data-tag attribute set to either to-do or to-do:completed, depending on whether the checkbox is checked or not. HtmlAgilityPack, the provider of the HtmlDocument and HtmlNode objects used above, provides good HTML-to-object mapping functionality. I can recommend it for this kind of task.

Authenticating the GraphServiceClient

The classes we have looked at so far show you how to interact with Graph API using the GraphServiceClient, but that client still needs to be authenticated to access a user’s Office 365 data.

As mentioned in the authentication post, we need to add the user’s access token to the Authorization header of any HTTP request the GraphServiceClient makes on our behalf. In the demo app, this is done by the GraphServiceHelper class. Here is the GetAuthenticatedClient method that our ToDoService class uses to acquire an authenticated client.

// Returns a GraphServiceClient with the user's access token set to the HTTP auth header.
public static GraphServiceClient GetAuthenticatedClient()
{
    if (client == null)
    {
        client = new GraphServiceClient(
            new DelegateAuthenticationProvider(
                async (requestMessage) =>
                {
                    // Get access token.
                    string accessToken = await GraphAuthProvider.Instance.GetUserAccessTokenAsync();

                    // Set access token to HTTP auth header.
                    requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
                }));
    }

    return client;
}

The Graph client itself does not include any authentication mechanism, instead, it provides the DelegateAuthenticationProvider for you to define whatever suits your app. The code you provide here will be used to authenticate each request the client makes. Here we simply add the access token we have cached for the user session to the Authorization header.

Accessing the cached access token

You can see in the code sample above that the meat of the logic is in the GraphAuthProvider class. Let’s dive into the method used:

public async Task<string> GetUserAccessTokenAsync()
{
    string userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
    HttpContextWrapper httpContext = new HttpContextWrapper(HttpContext.Current);
    TokenCache userTokenCache = new SessionTokenCache(userId, httpContext).GetMsalCacheInstance();

    ConfidentialClientApplication cca = new ConfidentialClientApplication(
        clientId, redirectUri, new ClientCredential(clientSecret), userTokenCache, null);

    // Attempt to retrieve access token from the cache. Could also make a network call for a new
    // access token if the cached one is expired or close to expiration and a refresh token is
    // available.
    try
    {
        AuthenticationResult result = await cca.AcquireTokenSilentAsync(
            scopes.Split(new char[] { ' ' }), cca.Users.First());

        return result.AccessToken;
    }
    // Unable to retrieve the access token silently.
    catch (Exception)
    {
        HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(
            new AuthenticationProperties() { RedirectUri = "/" },
            OpenIdConnectAuthenticationDefaults.AuthenticationType);

        throw new Microsoft.Graph.ServiceException(
            new Microsoft.Graph.Error
            {
                Code = Microsoft.Graph.GraphErrorCode.AuthenticationFailure.ToString(),
                Message = "Authentication is required."
            });
    }
}

The method uses the user’s identity to get the associated MSAL token cache, which securely stores the access token and refresh token of the user. We build a ConfidentialClientApplication from the application settings (including Azure AD client credentials) and the token cache. This MSAL object encapsulates the process of acquiring/refreshing an access token with the authorization code flow, as defined by OAuth 2.0. For a more detailed breakdown see: Authenticating with Azure AD and OpenID Connect.

The ConfidentialClientApplication is capable of releasing the access token of the user from the cache if it has previously been acquired and is still valid. Alternatively, if the existing access token has expired or is close to expiry, it could use a refresh token to acquire another. If neither of these options is possible, the user would need to authenticate with Azure AD again.

Closing thoughts

And that’s just about all there is to it. You should be able to adapt these fundamentals to whichever Office 365 app you wish to automate. Do let me know if you build anything cool with Graph API or if you found this guide useful!

Useful resources and acknowledgments

The following links were helpful in my own experimentation with Graph API, inspired various parts of my demo app code and/or contributed to my understanding. They can certainly help you too.

Hat-tip to the authors of the above.

About the Author Kirk MacPhee

An experienced software developer and technical lead, specializing in automation technologies and their application.

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s