Office 365 and the Graph API: Under the Hood

Office Graph In my previous post, I proposed an example application that leverages the resources available to us in Office 365 development platform and Azure Active Directory, as well as the in-application integration of Office 365 Add-ins.

Now we’ll take a deeper look at the Graph API and some of the implementation points.

Build Your Enterprise Graph

The Graph API empowers developers and enterprises to build new relationships and interactions between resources in Azure Active Directory, Office 365, and other applications and data assets.

As Microsoft’s enterprise cloud offerings continue to expand, so will the opportunities to weave these resources together in new and innovative ways. Microsoft’s acquisition of LinkedIn will help it expand its social network graph, so it will be interesting to see how it plays into its Graph API in the future.

The Graph API

Microsoft’s new Graph API provides unified access to Microsoft cloud services including Office 365 and Azure Active Directory resources, all with one endpoint and one security token. This simplifies implementation compared to the previously released and separate Azure Active Directory Graph API and Office 365 APIs.

Version 1.0 of the Graph API includes access to resources like:

  • Active Directory Users and Groups
  • Mail
  • Calendar
  • Contacts
  • Organizational Hierarchy
  • OneDrive – including keyword search for files
  • Webhooks – delivers notifications of changes to resources

Future releases of the Graph API (now in beta) plan to add even more functionality and resources such as:

  • OneNote notebooks and notes
  • Tasks
  • Identity Protection

One Identity: Azure Active Directory

Azure Active Directory (AAD) provides authentication and authorization services for Office 365. The same Azure Active Directory credentials are used to secure all Graph API calls, and the same identity can be easily integrated into your applications to enable single-sign on to apps and to secure web APIs.

For most applications and Office add-ins, the user will sign in with their credentials and the resulting token is used when making calls to the Graph API. However, you can also configure credentials for an application to allow for app-only access to Office 365 for processes like triggered services and web jobs.

Securing the Web Application and API with AAD

Both our Work Item web application and API are secured with Azure Active Directory (AAD), so users can log in with the same enterprise credentials that they use for Office 365 applications.

AAD supports the OAuth 2.0 protocol and OpenID Connect identity layer. To implement this in ASP.NET, we can use OWIN (Open Web Interface for .NET) middleware. Following the example of these web application and web API samples, our startup code can look something like this:

public void ConfigureAuth(IAppBuilder app)
{
    ConfigProvider configProvider = ConfigProvider.Instance();

    // configure OpenID Connect with AAD for MVC controllers
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
    app.UseCookieAuthentication(new CookieAuthenticationOptions());
    app.UseOpenIdConnectAuthentication(
        new OpenIdConnectAuthenticationOptions
        {
            // the application client ID as configured in Azure Active Directory
            ClientId = configProvider.ClientId,
            // the token authority, for example:
            // https://login.microsoftonline.com/mytenant.onmicrosoft.com
            Authority = configProvider.Authority,
            // if the user logs out from your app, they are redirected to this URI
            PostLogoutRedirectUri = configProvider.PostLogoutRedirectUri,
            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                AuthenticationFailed = context =>;
                {
                    context.HandleResponse();
                    context.Response.Redirect("/Error?message=" + context.Exception.Message);
                    return Task.FromResult(0);
                }
            }
        });

    // configure OpenID Connect with AAD for web api
    app.UseWindowsAzureActiveDirectoryBearerAuthentication(
        new WindowsAzureActiveDirectoryBearerAuthenticationOptions
        {
            TokenValidationParameters = new TokenValidationParameters()
            {
                // the same AAD client ID as above
                ValidAudience = configProvider.ClientId
            },
            // the O365 tenant for your organization, e.g. mytenant.onmicrosoft.com
            Tenant = configProvider.Tenant
        });
}

Authorizing the Web Job for Office 365 Access

The Web Job that does the scheduling and email notifications needs to access Office 365 resources as a service, that is, without a user manually logging in with their credentials. For this we use the OAuth 2.0 client credentials grant flow.

After configuring the app in Azure Active Directory for app-only authentication, we can obtain an access token using an HTTPS call to AAD’s OAuth 2.0 token API:

class OauthTokenResponse
{
    public string expires_on { get; set; }
    public string access_token { get; set; }
}

private async Task<OauthTokenResponse> GetGraphAccessTokenWithHttpAsync(string tenant, string clientId, string clientKey)
{
    // the authority that will issue the token
    var authority = string.Format("https://login.microsoftonline.com/{0}", tenant);
    // the address to the OAuth2 token endpoint
    var address = authority + "/oauth2/token";
    // the resource to which we want access
    var resource = "https://graph.microsoft.com";
    using (var httpClient = new HttpClient())
    {
        using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, address))
        {
            request.Content = new FormUrlEncodedContent(
                new[]
                {
                    new KeyValuePair<string, string>("grant_type", "client_credentials"),
                    new KeyValuePair<string, string>("client_id", clientId),
                    new KeyValuePair<string, string>("client_secret", clientKey),
                    new KeyValuePair<string, string>("resource", resource)
                });
            var response = await httpClient.SendAsync(request);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsAsync<OauthTokenResponse>();
        }
    }
}

Rather than making our own HTTPS call with our own model we can use the ADAL library:

private async Task<AuthenticationResult> GetGraphAccessTokenWithAdalAsync(string tenant, string clientId, string clientKey)
{
    // the authority that will issue the token
    var authority = string.Format("https://login.microsoftonline.com/{0}", tenant);

    // the resource to which we want access
    var resource = "https://graph.microsoft.com";

    // aquire the token from the authority using the client ID and key
    // configured in Azure Active Directory
    AuthenticationContext authenticationContext = new AuthenticationContext(authority);
    var clientCredential = new ClientCredential(clientId, clientKey);
    var result = await authenticationContext.AcquireTokenAsync(resource, clientCredential);

    return result;
}

Calling the Graph API

Once we have the access token, we can get a list of users in our directory like so:

public class GraphListResponse<T>
{
    public List<T> Value { get; set; }
}

public class GraphUser
{
    public string Mail { get; set; }
    public string DisplayName { get; set; }
}

public async Task<string[]> GetUsersDemoAsync()
{
    // Use the Azure AD API to get users
    var accessToken = await this.GetAccessTokenAsync();

    var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0}", accessToken));
    var response = await httpClient.GetAsync("https://graph.microsoft.com/v1.0/users");
    response.EnsureSuccessStatusCode();
    var result = await response.Content.ReadAsAsync<GraphListResponse<GraphUser>>();
    List<GraphUser> users = result.Value;

    // Filter out users with no SMTP mail address
    return (from u in users where !string.IsNullOrWhiteSpace(u.Mail) select u.Mail).ToArray();
}

Here’s an example of checking the calendar for a user. For brevity’s sake, we’ll just use a simple check for any events on that day that do not have a status of “Free”:

public async Task<bool> IsAvailableDemoAsync(string emailAddress, DateTime day)
{
    bool available = true;

    // filter for only calendar events occuring on the requested day
    var startOfDay = day.ToUniversalTime().Date;
    var endOfDay = startOfDay.AddDays(1);
    var graphEndpoint = "https://graph.microsoft.com/v1.0/";
    var address = Uri.EscapeUriString(string.Format(
        "{0}users/{1}/calendarview?startDateTime={2:o}&endDateTime={3:o}&$select=Subject,ShowAs,IsAllDay,Start,End",
        graphEndpoint,
        emailAddress,
        startOfDay,
        endOfDay));

    var accessToken = await this.GetAccessTokenAsync();
    var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0}", accessToken));
    var response = await httpClient.GetAsync(address);
    response.EnsureSuccessStatusCode();
    var result = await response.Content.ReadAsAsync<GraphListResponse<CalendarEvent>>();

    var calendarEvents = result.Value;
    foreach (var calendarEvent in calendarEvents)
    {
        if (calendarEvent.ShowAs != "Free" && calendarEvent.End.DateTime != startOfDay)
        {
            available = false;
            break;
        }
    }

    return available;
}

Finally, here is an example of sending an email:

public async Task SendEmailDemoAsync(string fromEmailAddress, EmailMessage message)
{
    var graphEndpoint = "https://graph.microsoft.com/v1.0/";
    string address = String.Format("{0}users/{1}/sendmail",
        graphEndpoint,
        fromEmailAddress);

    var content = new
    {
        Message = message
    };

    var accessToken = await this.GetAccessTokenAsync();
    var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0}", accessToken));
    await httpClient.PostAsync(address, content, new JsonMediaTypeFormatter());
}

If you are using .NET, you can save the trouble of building your own model classes and making direct HTTP calls by using the Microsoft Graph .NET Client Library, and there are SDKs for other platforms coming soon as well.

Platform Independence

In these examples I’ve used C# and .NET, but calls to the Azure Active Directory token API and Graph API are standard HTTP methods – so you can call them from any language and platform that supports HTTP calls.

Looking Ahead

In this article we looked at some implementation details for integrating Azure Active Directory security and the Graph API for access Active Directory and Office 365 resources in our web application. In the final installment in this series, we’ll take a look at the Outlook Add-in details.

Further Reading from our Office 365 Series:

Office 365 as a Development Platform

Matter Center on the Office 365 Platform

Unlocking the Enterprise in Office 365

Improving Productivity With Office 365 Add-Ins

About David Zientara

David Zientara joined AIS in 2010 with over 15 years of software engineering and architecture experience, much of it utilizing the Microsoft technology stack. His career has spanned industries such as security, digital imaging, health care, education and non-profits with organizations ranging from small start-ups to multi-national corporations. His enthusiasm is currently focused on HTML5 and JavaScript front ends, server-side solutions using technologies like ASP.NET MVC and Web API, and leveraging cloud services. David’s other passions include music, foreign languages and cultures, and travel.