Azure Active Directory in Office 365 (Best Tutorial 2019)

Azure Active Directory

Azure Active Directory and security

This tutorial explains the architecture and the capabilities of Microsoft Azure Active Directory. You will learn how the authentication and authorization engine of the Microsoft Graph API works and how to provision custom applications and services that consume the Microsoft Graph API securely.

 

Introducing Azure Active Directory

To understand how the Microsoft Graph API works, you need to figure out the security architecture of the provided services. All the services available through the Microsoft Graph API share a common underlying security infrastructure, which is based on Microsoft Azure Active Directory (Azure AD) that uses OpenID Connect and OAuth 2.0 protocols for authentication and authorization.

 

The Azure AD services are offered on a per-Office 365 tenant basis, and there is a direct mapping and relationship between an Azure AD service instance and the identities managed within a specific tenant and with a Microsoft Office 365 tenant. Azure AD is a cloud-based directory service that sits under the cover of any Office 365 tenant.

 

Azure AD is used to store users’ identities, authenticate them, and federate tenants with third-party identity providers like Microsoft Active Directory Federation Services (ADFS) on-premises. Moreover, in the business-to-consumer offering (known as Azure AD B2C) it is an open identity provider that can be configured as an intermediary for authenticating users through external identity providers like Faceblog, Google, Microsoft Account, LinkedIn, Amazon, and any other provider that is compliant with Open ID Connect, OAuth 2.0, or WS-Federation. 

 

One more option is Azure AD Business-to-Business (known as Azure AD B2B), which allows companies to share applications and data with business partners by allowing those partners to use their self-managed identities. Internally, Azure AD supports all of these authentication or authorization protocols and provides a rich set of endpoints to consume them.

 

Moreover, Azure AD allows you to configure custom apps and services that can leverage its authentication and authorization infrastructure to securely provide services and capabilities to users’ identities stored in Azure AD. The beauty of Azure AD is the capability to consume it through a set of REST API, which are secured via OAuth 2.0 and Azure AD, as with third-party services and apps that Azure AD supports. there is a rich REST-based Graph API that allows browsing the objects defined in the directory and creating, updating, or deleting objects, services, and security-related objects.

 

Azure AD is available for free in its entry-level offering and is always included in every Office 365 tenant. If you like, you can pay for Azure AD to have access to more advanced features like multifactor authentication, more advanced users and passwords management tools, group-based access management, and many more capabilities.

 

For further details about the capabilities and services Azure AD offers based on the available offerings, you can read the online documentation, which is available at the following URL: Active Directory - Access & identity - IDaaS | Microsoft Azure. 

 

Identities in Azure AD

Whenever you deploy an Azure AD tenant, whether manually or automatically within the creation of a new Office 365 tenant, you can manage users’ identities. From an architectural perspective, there are three flavors of identities:

 

Cloud identities These are cloud-only identities that are created, managed, and handled completely by Azure AD. This is the best option for small businesses or widely geographically distributed companies that don’t want to have an on-premises directory system or don’t have an on-premises infrastructure at all. It is also a good option for applications that want to support OpenID Connect, OAuth, or SAML through a cloud-based offering.

 

Synchronized identities These are identities that get synchronized between on-premises and the cloud. Microsoft provides tools that enable you to synchronize objects between an on-premises directory system and an Azure AD tenant. This is a suitable solution for businesses that have an on-premises infrastructure, including a directory system—for example, Microsoft Windows Active Directory—and are willing to share users’ identities across on-premises and the cloud, including the authentication credentials (user names and hashes of passwords).

 

In this scenario, end users will consume cloud services—for example, Office 365—using a completely cloud-based security infrastructure, which under the cover is synchronized with the on-premises environment. Users will authenticate against the Azure AD authentication infrastructure, providing credentials that can be a replica of those defined on-premises and having the Same Sign-On experience, which allows them to access the cloud services by using the same credentials they have on-premises.

 

From a management perspective, some policies or password rules can vary between on-premises and the copied synchronized identities on the cloud. Moreover, it is fundamental to highlight that the synchronization process does not copy users’ passwords to the cloud. Rather, it stores a hash of their passwords in the cloud.

 

Federated identities This is a more advanced scenario, in which you can federate Azure AD with your on-premises infrastructure. In an on-premises Microsoft Windows Active Directory infrastructure, you will leverage the Microsoft ADFS feature of Microsoft Windows Server to federate your local Active Directory with Azure AD. In this scenario, end users will be able to leverage a Single Sign-On (SSO) logon approach, which will improve their overall experience.

 

This is often the best option for large businesses that want to have a centralized identity management system and provide a consistent end user experience. From a management perspective, the on-premises directory system is the unique repository of policies and password rules, providing a more consistent management experience. The users’ authentication process will involve services and workloads hosted on-premises instead of using the classic Azure AD authentication form.

 

These three options can be mixed within the same Azure AD tenant according to your needs. You can also start with one identity management model and transition to another one almost transparently from your end users’ perspective.

 

Managing Office 365 identities

From an Office 365 perspective, you can manage your users’ identities from the admin portal, which is available at the URL Sign in to your account under the Admin Center application, by selecting the Users > Active Users menu item in the left command tree.

 

There, you will be able to add, update, or delete users and map them with Office 365 licenses. Under the cover, that management UI will store users’ information in the related Azure AD tenant. To see that, you can click the Azure AD link, which is available in the left command tree of the Admin Center in the group of menu items called Admin Centers. When you click the Azure AD menu, your web browser will be redirected to the Azure management portal. There, you will be able to completely manage the Azure AD tenant related to your Office 365 tenant.

 

As you can see in Figure, after selecting the Active Directory section and choosing your target Azure AD tenant, in the Azure AD management UI there is a tab called Users. From this tab, you can manage the same users’ identities as in the admin portal of Office 365, but you can do even more. If you click any user’s identity, you can see and manage the user’s profile information, including the work information and the devices used to log on (only if you purchase and enable the Premium features), and you can read a log of the user’s recent activities and logons.

 

In general, within the Azure AD administrative UI provided by the Azure management portal, you have the following sections:

Users Manage all the users’ information and configuration.

 

Groups Configure users’ groups.

Applications Define applications and services, which will rely on the current tenant for users’ authentication and authorization. Domains Configure the list of Internet domains associated with the current tenant.

 

Directory Integration Provision directory integration between the current tenant and one or more on-premises directory services. For instance, from this section you can configure Microsoft Windows Active Directory domains synchronized or even federated with the current tenant.

 

Configure Allows configuring the tenant name, branding the login page, users’ password reset rules, users’ and devices access rules, and much more. Reports Monitor the tenant activities through advanced reports.

 

Licenses Through this section, you can upgrade to Azure AD Premium or buy the Enterprise Mobility Suite, which includes tools like Microsoft Intune and Azure Rights Management.

In the following sections, you will learn how to manage apps in Azure AD, which is the main focus of this blog.

 

Configuring apps and services in Azure AD

To leverage the capabilities of Azure AD within your apps and services, you need to register those apps and services in the target tenant. Any apps or services relying on an Azure AD tenant for authentication and authorization services will need an issued Client ID and, optionally, a shared secret.

 

To achieve this result, you have two options:

  • Registering the app manually through the web management UI of Azure AD
  • Provisioning the app automatically by leveraging the capabilities offered by Microsoft Visual Studio 2015

In the following sections, you will learn how to leverage both of the above scenarios.

 

Manual configuration

To manually configure an app or service to rely on Azure AD, you have to access the Azure AD management portal and click the Applications tab. From there, you will be able to browse the already registered apps, or you can add other custom apps or services.

 

Before adding a new app, let’s browse the directory to see what is available out of the box. If your Azure AD tenant is associated with an Office 365 tenant, you will have some apps already registered:

  • Office 365 Exchange Online
  • Office 365 Management APIs
  • Office 365 SharePoint Online
  • Office 365 Yammer

 

Those are the native apps registered by Office 365 to provide the Office 365 services. You cannot manage or delete them; you can only see that they are registered.

 

Now, let’s click the Add button in the lower-middle area of the Azure AD management UI. An app registration wizard, will prompt you to select whether you want to “Add An Application My Organization is Developing” or “Add An Application From The Gallery.” The first option is the one in which you are interested because it will allow you to register a custom application. The second option will allow you to browse the Application Gallery and add an app from the online marketplace. At the time of this writing, there are more than 2,500 apps available in the gallery.

 

Select the first option (Add An Application My Organization Is Developing), and you will have to provide a name for your app and choose between a Web Application And/Or Web API or a Native Client Application. The former defines an application that will have a web-based UI. The latter defines any native platform application—for example, those for desktops, tablets, or smartphones.

 

For instance, name the app Programming.Office365.SampleApp, select Web Application And/Or Web API, and make a step forward by clicking the arrows in the bottom-right corner of the page. You will have to provide a Sign-On URL for the app, which is the URL that the end users will use to access your app. You can also provide an App ID URI, which has to be a valid unique URI.

 

Now, you are almost done. The wizard will register the app in Azure AD and generate a unique Client ID (a GUID) for that app.

 

Dashboard Here, you can see a recap of the configuration parameters. You can see all the available endpoints (Metadata, WS-Federation, SAML-P, OAuth 2.0, Azure AD Graph) available to access the app through the current Azure AD tenant. You can upload a custom logo for your app, and you can manage the app manifest, which will be covered in more detail later in this blog. You can delete the app if you don’t want to use it anymore.

 

Users Through this tab, you can assign or remove the right to access the app to the users of the current Azure AD tenant.

Configure This tab includes the most useful options and configuration parameters, including the Sign-On URL, the ClientID, the shared secret (if any), the Reply URL, and so on. The Reply URL is the list of URLs to which the application can redirect users after the authentication phase. Moreover, you will find the permissions for the app. This tab will be explored in more detail later in this section.

 

Owners This tab allows you to define the users within your directory who will be owners of the current app. It is a functionality that is under preview at the time of this writing.

 

Let’s focus on the Configure tab. The first information you can view or edit within that tab is the name and the Sign-On URL of the app. Moreover, you can configure whether the app will behave as a multitenant app, which is a concept that will be explained in much more detail in the next section of this blog. For now, configure the sample app as multitenant; you will leverage this capability later. Then, you have the Client ID, which is fundamental from an OAuth perspective. It is read-only and can only be copied to the clipboard.

 

Just after the ClientID, you can see or configure the flag User Assignment Required To Access App, which if true allows you to assign the application to a specific set of users in Azure AD instead of targeting every user of the Office 365 tenant. Next, there is the section through which you can create security keys for the app.

 

You can create as many keys (also known as Client Secret) as you want, and when you create a new key you can define if it will expire after one or two years. The key value will be available after saving the configuration tab. Be careful that the key value of a newly generated key will be visible only one time, just after its creation. Store it in a safe place just after creation. If you lose the key value, you will have to generate a new one. If you want to follow the flow of this blog, save the Client Secret because you will use it soon.

 

Following the keys, there is the Single Sign-On section where you can define the App ID URI and the URLs to which Azure AD will consent to redirect the end users after authentication. Here, you can configure as many URLs as you like, and usually you configure the production URL of the app and any development, testing, or staging URL.

 

The last section is the Permission To Other Applications, which is one of the most important sections. From here, you can configure what services and applications will be accessible by the current app and the related custom permissions for the current app against every other accessible app. To configure a permission, click the Add Application button and search for the application that you want to configure. By default, every app will be configured to access the Azure AD services to sign in users and read their profile.

 

For example, to configure the just-created app to access Microsoft SharePoint Online, you can click the Add Application button, select the Office 365 SharePoint Online application, and click the Complete button in the lower-right corner of the dialog. To access the Microsoft Graph API, click the Add Application button and select the Microsoft Graph application. You can also add third-party applications, not only those released by Microsoft. Moreover, you can add your own applications if you want to consume a custom set of API that you have developed and provisioned on Azure AD.

 

You can now configure two groups of permissions for the application that you added:

Application permissions These are the permissions that pertain to the app by itself, and they apply whenever the custom app accesses the other app with an app-only token (on behalf of the app only).

 

Delegated permissions These are the permissions that are granted to the app when delegating an end user, and they apply whenever the custom app accesses the other app on behalf of a specific user. In this case, the final set of permissions granted to the app will be the intersection of these permissions and those of the user.

 

The permissions that can be granted to any app are customizable based on the app, and you can also configure your own custom permissions for your custom apps. As you can see, there are permissions for reading, writing, and managing resources like files, sites, mail, contacts, groups, and so on.

 

Multitenancy

Whenever you register an app in Azure AD, you are in a specific tenant. If you want to provide your app to multiple customers who will have different Azure AD tenants, you can configure your app as a multitenant app.

 

As you have seen in the previous section, there is a switch in the Configure tab of any app that you can use to configure multitenancy support. The result of this action will be the capability to support multiple tenants with a unique app registration. From your customers’ perspective, they will have to sign up their tenant to be able to leverage your app.

 

To follow the flow of this blog, you should enable multitenancy for the test app you are registering in your tenant. This choice will become useful in the upcoming sections. Whenever you register an app as multitenant, you have to pay attention on the value of the AppID URI, which has to be unique and should identify the domain name of the main tenant in which you are registering the app.

 

The sign-up process will require your customers to trust your app and to consent for it to have those rights—against their domains and/or users’ accounts—that you declared in the permissions setting. Usually, the sign-up process has to be handled by a user who has administrative rights on the target Azure AD tenant. This way, the application will be available to all the users of the target tenant. However, even a single user can sign up for personal use of a specific application.

 

By signing up a tenant, your multitenant app will be configured automatically in that target tenant and, just after that, the tenant administrators will be able to manage, and even remove, your app from their tenant.

 

Using Microsoft Visual Studio

Another suitable option for registering an app in Azure AD is to use Microsoft Visual Studio 2015 or Microsoft Visual Studio 2013 Update 2 or later. If you create a new Visual Studio project, which could be a native app (like Windows Forms, WPF, or Universal Windows App), you easily can configure that app as an application in Azure AD.

 

To follow this process, let’s create a new The ASP.NET MVC website in Microsoft Visual Studio 2015. In the New ASP.NET Project creation wizard, select to configure users’ authentication by clicking the Change Authentication button. You will see a wizard that allows you to choose from four available users’ authentication options.

 

The available options are:

No Authentication The name should be clear, but it means that your web app will be accessible to anonymous users and there will not be any authentication technique configured.

 

Individual User Accounts Allows you to manage identities that are related only to the current application. Users can register and sign in with the app, or they can sign in using their existing Faceblog, Twitter, Google, or Microsoft account or any other provider’s account.

Work And School Accounts Leverages Azure AD or ADFS through OWIN for Microsoft ASP.NET Site.

Windows Authentication Relies on an on-premises Active Directory domain for users’ authentication via Windows integrated security.

 

To leverage the Azure AD service for authenticating your users, you will have to choose the Work And School Accounts option. Moreover, you will have to properly configure the tenant name and the application unique name for your app. You can also choose whether to use Azure AD single tenant, which is presented as Cloud – Single Organization; Azure AD multitenant, which is the Cloud – Multiple Organizations option; or an on-premises ADFS server, which is presented as On-Premises.

 

In this example, select Cloud – Single Organization to use Azure AD with a single-tenant application. After configuring the Work And School Accounts option and creating the Visual Studio project, the project creation wizard will automatically register your project in Azure AD as a new application, which will have the right to access Azure AD for authentication purposes and read users’ information from Azure AD. The project creation wizard will also add some configuration items to the .config file of your project. In the following code excerpt, you can see those configuration items.

<appSettings>
<add key="ida:ClientId" value="07974dc3-ae70-4f56-80eb-1feac570e15d" /> <add key="ida:AADInstance" value="Sign in to your account" />
<add key="ida:ClientSecret" value="***************************************" />
<add key="ida:Domain" value="http://tenant.onmicrosoft.com" />
<add key="ida:TenantId" value="6c94075a-da0a-4c6a-8411-badf652e8b53" /> <add key="ida:PostLogoutRedirectUri" value="https://localhost:44300/" />
</appSettings>

Here is a brief explanation of the configuration items:

ida:ClientId Represents the ClientId from an OAuth 2.0 perspective

ida:AADInstance Defines the base URL of the authorization server, which is always Sign in to your account in Azure AD

ida:ClientSecret The shared secret of the app registered in Microsoft Azure AD ida:Domain Represents the Azure AD reference domain name

ida:TenantId Defines the tenant ID, which can be concatenated to the ida:AADInstance argument

ida:PostLogoutRedirectUri The URL to which the browsers will be redirected in case of logout

 

If you now start the web application that you have just created, before accessing the home page you will be asked to log in using your Office 365 tenant credentials. After a successful login, you will have to grant (consent) the permissions to the app. Just after the authentication and after accepting to grant those rights to the app, you will be back to your web application with an authenticated session for the user’s account you used.

 

Understanding OpenID Connect and OAuth 2.0

Before leveraging the Azure AD service for consuming the Microsoft Graph API, it is useful to understand the protocols involved and how they work, aside from any specific programming language or development environment. If you don’t like to learn about the inner workings of the involved protocols, you can skip this section or you can read it later, when you need to satisfy some geeky curiosity.

 

First of all, the suggested protocol for users’ authentication against Office 365 is OpenID Connect, which is an identity layer on top of the OAuth 2.0 protocol, as stated on the OpenID Connect site (OpenID Connect | OpenID). The OpenID Connect protocol allows authenticating users across websites and native apps in a flexible and technology/platform independent manner that leverages REST/JSON messages flows. The goal is to allow developers to authenticate end users to let them consume specific services without having to manage or own their credentials (mainly user names and passwords).

 

The SAML (Security Assertion Markup Language) token format and the WS-Federation

protocol have been—and continue to be—available to authenticate users. However, those technologies mainly target web-based applications and manage XML-based tokens, which are not very mobile friendly and flexible. In contrast, OAuth 2.0 and OpenID Connect were created with cross-platform, interoperability, multidevice, and flexibility in mind.

 

You may be wondering why there is another protocol on top of OAuth 2.0 and why we cannot use OAuth 2.0 directly for authentication. First, you need to keep in mind that OAuth is an authorization framework, not an authentication protocol. Moreover, the goal of OAuth 2.0 is to grant access to resources through an access-granting protocol, not to authenticate subjects.

 

Since February 26, 2014, the OpenID Connect specification is a released standard, and companies like Google, Microsoft, PayPal, and others are releasing technologies that are self-certified to be compliant with the specification.

 

As already stated, under the cover of OpenID Connect there is OAuth 2.0, which can be understood by reading its definition on the IETF (Internet Engineering Task Force) site. If you browse to the OAuth 2.0 specification URL (The OAuth 2.0 Authorization Framework), you will find the following sentence:

 

The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf.

 

From an Office 365 perspective, the third-party application is any Office application or SharePoint Add-in or any other software that will consume the Microsoft Graph API. The Microsoft Graph API by themselves are the HTTP service to which the previous sentence refers. The limited access defined in the above description is an OAuth 2.0 granted access token, which can be used to act on behalf of a specific user who is the resource owner or on behalf of the add-in or software by itself when OAuth 2.0 grants an app-only OAuth token. Thus, in the Office 365 world, we can rephrase the previous sentence like this:

 

The OAuth 2.0 authorization framework enables any add-in or software solution to obtain a limited access token to access the Microsoft Graph API, either on behalf of a user by orchestrating an approval interaction between the user and the Microsoft Graph API, or by allowing the add-in or software solution to obtain an app-only access token to act on its own behalf.

 

Just after a user authenticates through OpenID Connect within an add-in or a software solution, the security infrastructure of Office 365 will engage the OAuth 2.0 protocol to grant an access token to consume the Microsoft Graph API.

 

The OpenID Connect communication flow

To better understand how the OpenID Connect specification works, you need to consider the communication flow built on top of OAuth 2.0 as an extension of its authorization process. As you can read in the OpenID Connect Core Specification 1.0, the authentication flow is based on five fundamental steps. 

 

For further details about the OpenID Connect Core Specification 1.0, you can read the online documentation available at the following URL: OpenID Connect Core 1.0 incorporating errata set 1.

 

In that schema, the RP stands for Relying Party and represents the Client node, and the OP stands for OpenID Provider and corresponds to the authentication server/infrastructure. In OpenID Connect Core Specification 1.0, the OP can also provide a UserInfo Endpoint.

 

At the beginning of the flow, the RP sends an authentication request to the OP. After that, the OP authenticates the end user and obtains an authorization to consume a specific resource. Then, the OP answers the authentication request by providing an ID token, which is represented as JWT (JSON Web Token) token, and usually there is also an access token. Then, the RP can send a request, including the access token, to the OP to retrieve the UserInfo, which are claims about the current user. At the end, the OP sends the UserInfo back to the RP.

 

From a Microsoft Azure AD and Office 365 viewpoint, the flow you have just seen can be represented like in Figure.

First, the application—whether it is a SharePoint Add-in, an Office Add-in, an Office 365 application, or something else—will contact an Azure AD authorization endpoint to authenticate the end user and to request an authorization code. The Azure AD authorization endpoint is the webpage that authenticates the end user and asks to grant (consent) the application to access a user’s resources on his own behalf, if any authorization is needed.

 

After a successful authentication and authorization/consent, the application will get back an authorization code. At this time, the application will be able to send a token request to the Azure AD token endpoint, providing a reference to the resource to access and the just-assigned authorization code.

 

The Azure AD token endpoint will send back an ID token (it is a JWT token) together with an access token and a refresh token. If the application just needs to authenticate the end user, the process is completed and the ID token will suffice. If the application also needs to consume any further API/service/resource, like the Microsoft Graph API, then it will be able to use the access token to invoke the target service or API.

 

Under the cover of OpenID Connect and OAuth 2.0

The best way to understand the flow described in the previous section and to inspect what is happening under the cover is to use a tool like Fiddler and look at what’s happening on the wire. Fiddler is a free web debugging proxy. It is a fundamental tool for creating real web-based and REST-based solutions.

 

Let’s go into the Azure AD management portal, select a target app (like the one you have just registered), and click the View Endpoints command button that is available in the lower part of the screen. A dialog like the one shown in Figure will appear, presenting the entire list of endpoint URLs for consuming the app. Here is the full list of available endpoints:

 

Federation Metadata Document Represents the URL for the federation metadata XML document the app uses for authentication through Azure AD

  • WS-Federation Sign-On Endpoint Declares the URL to use for sending sign-on and sign-out requests using the WS-Federation protocol
  • SAML-P Sign-On Endpoint Defines the URL to use for sending SAML-P sign-on requests
  • SAML-P Sign-Out Endpoint Defines the URL to use for sending SAML-P sign-out requests

 

Microsoft Azure AD Graph API Endpoint Represents the endpoint at which an application can access directory data in your Windows Azure AD directory by using the Microsoft Graph API

  • OAuth 2.0 Token Endpoint Declares the URL at which an app can obtain an access token according to the OAuth 2.0 protocol
  • OAuth 2.0 Authorization Endpoint Defines the URL at which an app can obtain an authorization code according to the OAuth 2.0 protocol

 

As you can see by reading the declared URLs, all the addresses include a GUID that is the tenant ID, which varies for every Azure AD tenant. If your app is configured to support multitenancy, and if you want to use these URLs independently from any specific tenant, you can replace the GUID with the common keyword.

 

Getting an authorization code

Now let’s say that you want to manually play with OAuth 2.0 and OpenID Connect to consume the Microsoft Graph API. Copy the URL of the OAuth 2.0 authorization endpoint and add some query string parameters, as shown here:

 

Sign in to your account[RedirectURL]&response_type=code&client_id= [ClientId]

As you can see, in the above URL there is the common keyword instead of the tenant GUID because the sample app you are using should be configured as a multitenant app. Moreover, there are some variable arguments that the OAuth 2.0 protocol expects, like the redirect_uri, the response_type, and the client_id. The redirect_uri argument should assume as a value any of the Reply URL values defined in the Configure panel of the app.

 

The response_type argument can assume a value of code whenever you want to get back an authorization code, which is the case in the current example. However, code is not the only accepted response type from an OpenID Connect specification perspective. Last, the client_id argument can be copied from the Client ID available in the Configure panel of the app.

 

For further details about the available response types, you can read the online documentation for the OpenID protocol available here: OAuth 2.0 Multiple Response Type Encoding Practices.

 

Replace the tokens with proper values according to the above explanation. Then, open a new browser session using the Private Browsing or the Incognito mode or at least without any logged-in user session from a Microsoft Azure or Office 365 perspective. Paste the customized OAuth 2.0 authorization URL into the browser’s address bar. As you will see, your browsing session will be redirected to the Azure AD login page, which is the Azure AD authorization endpoint.

 

For a better understanding of what is happening under the cover, you can run the Fiddler tool to trace the HTTP(S) communication flow.

In this example, let’s authenticate using an identity related to the tenant in which you registered the app. If you provide a valid set of credentials, the browser will be redirected (HTTP Status Code 302) to the URL provided in the redirect_uri query string argument with the addition of a query string argument with name code, which will hold the authorization code released by the OAuth 2.0 server.

 

Putting the authorization code in the query string of the redirect_uri is the default behavior from an OpenID Connect Core specification perspective. Your browser will be redirected to a URL like the following:

 

[redirect_uri]?code=[Authorization Code]&session_state=[GUID]

For the sake of completing this sample journey, you should grab the value of the return authorization code and store it in a safe place.

 

In contrast, if you provide a valid set of credentials but configure the redirect_uri with a value that is not defined in the Configure panel of the app, you will get back an exception, even if you provide valid credentials during login. The exception will be something like the following:

AADSTS50011: The reply address ‘[your not properly configured redirect URL]’ does not match the reply addresses configured for the application: [the ClientID of your app].

 

Now, let’s play with multitenancy. First, if you try to use the app from a tenant that is not the one in which you have registered the app and have not configured the app to be multitenant, as soon as you authenticate providing third-party tenant’s credentials, you will get back the following error:

AADSTS70001: Application with identifier [the ClientID of your app] was not found in the directory [third party tenant].

 

If you properly configured the app to support multitenancy and authenticate providing credentials of a third-party tenant, before redirecting the browser to the redirect_uri the Azure AD infrastructure will ask you to grant (consent) permissions to the app, according to the permissions configured in the Permissions To Other Applications section of the Configure panel of the app. In Figure 4-16, you can see the webpage that asks for a user’s consent.

 

By clicking the Accept button, the app will be registered in the third-party tenant automatically, and the web browser will be redirected to the app website (redirect_uri) providing the authorization code, as in the previous example.

Now, your user has been authenticated and you have the authorization code.

 

Getting an access token

The next step to access the Microsoft Graph API securely is to get an access token. To do this, get the OAuth 2.0 token endpoint from the list of app endpoints in Azure AD. It should be something like the following:

 

Sign in to your account

Notice that the URL is still targeting a multitenant environment because of the common keyword instead of the tenant GUID in the URL path. Start any HTTP(S) client tool like the Fiddler Composer, cURL, or whatever else you like. Fire an HTTP POST request targeting that URL and providing a body like the following:

grant_type=authorization_code&redirect_uri=[redirect_uri]&client_id= [ClientID]&client_secret=[Cl

ientSecret]&code=[Authorization_Code]&resource=[target_resource_identifier]

 

The grant_type argument instructs the target authorization server about the kind of authorization grant that the client is presenting. In the current example, you are providing an authorization code, hence the authorization_code value for the grant_type argument. Moreover, the POST request will include information about the redirect_uri for the app requesting the token and the client_id and client_secret of the app (registered in the Configure tab of Azure AD) to authenticate the app, not only the user.

 

The authorization code will follow the app credentials included in the code argument. Last, there is a fundamental piece of information about the target resource/service that the app wants to consume: the resource argument, which generally is a unique URI representing the target resource/service. To consume the Microsoft Graph API, you should provide a value of Microsoft Graph to the token endpoint.

 

After sending the HTTP POST request, if the authorization code and the app credentials are valid and the request to consume the target resource is authorized, you will get back a JSON serialized response object, which will include the following properties:

 

For further details about OAuth 2.0 token response, you can read the following document: The OAuth 2.0 Authorization Framework.

access_token The requested access token, formatted as a Base64 encoded JWT token, that you can use to consume the target service. Store it in a safe place.

expires_in The lifetime in seconds of the access token. Usually, the access token released by Azure AD lasts for one hour.

 

If you want to learn more about tokens validation, you can read the document “Azure AD Token Lifetime,” which was written by Vittorio Bertocci and is available at the following URL: Azure AD Token Lifetime.

expires_on The access token expire date formatted in Unix time format, which is the number of seconds elapsed since 00:00:00 UTC of Thursday, January 1, 1970 (1970-01-01T0:0:0Z), not including leap seconds.

  • id_token A Base64 encoded JWT token that defines the user’s identity.
  • not_before The access token validity start date, represented in Unix time format.
  • refresh_token Optional; provides a token that can be used to refresh/renew access tokens when they expire.
  • resource The source for which the access token has been released.
  • scope The permissions that the access token contains/allows.
  • token_type The type of the access token. It is always Bearer.

 

Accessing a resource or a service

You are now ready to leverage the access token to access the target resource or service. To do that, you just need to copy the access token value and provide it as an Authorization header of type Bearer while executing the HTTP request against the target service. Let’s say that you want to consume the Microsoft Graph API and you want to retrieve some generic information about the currently logged-in user’s profile. You also want to access some files stored in the current user’s OneDrive for Business.

 

Thanks to the new architecture of the Microsoft Graph API, the access token you retrieved in the previous section can be used to consume all the API published through the Microsoft Graph endpoint. Start invoking the https://graph.microsoft.com/v1.0/me endpoint to retrieve a user’s profile information. You will have to make an HTTP request using the GET method and providing an authorization token like the following:

 

Authorization: Bearer [Access Token Value]

If you provided a valid access token (one that is not expired) that grants the right to retrieve the user’s profile information, you will get back a JSON response with some useful information like:

  • Business telephone, office location, mobile phone, and so on
  • Main email address
  • Preferred language for UI
  • Registered devices
  • Contacts and calendars
  • User principal name
  • Photo

 

Moreover, you can invoke the OneDrive for Business endpoints to retrieve, for example, the list of files in the user’s personal storage. To do this, you just need to use the same request format as before (GET with HTTP Authorization Bearer Header), targeting the URL https://graph.microsoft.com/v1.0/me/drive. This time, the response will be a JSON object with pointers to the root folder of the OneDrive for Business drive and to the items within the drive.

 

These examples illustrate the power of the new Microsoft Graph API. In the past and without leveraging the Microsoft Graph API, you had to retrieve one dedicated access token for each service endpoint. Moreover, you had to find the target service URI through the Office 365 Discovery Service. Now, thanks to the new unified model, you just need to retrieve one unique access token for the unified endpoint, and you are ready to consume almost every service with a unique security handshake.

 

Refreshing an access token

In the previous sections, you saw that an access token has a validity time frame, and maybe you are wondering what happens when an access token expires. You cannot use an expired token. However, when you request an access token through the OAuth 2.0 token endpoint by providing an authorization code, you usually also get back a refresh token.

 

That refresh token usually expires much later than the access token. The refresh token expiration depends on the authorization server configuration, and it can vary from hours (10 to 15 hours) to days or even months. At the time of this writing, Azure AD provides refresh tokens that last for 14 days and that will be renewed automatically for another 14 days whenever you use them within the 14 days of validity. You can repeat the process for up to 90 days.

 

After 90 days, you will have to repeat the authentication process from scratch. Refresh tokens for Microsoft account guest accounts last only 12 hours. Furthermore, a refresh token can suddenly become invalid for many reasons; for instance, when an end user changes her password, any previously released refresh token expires immediately.

 

In general, you cannot assume a specific and fixed lifetime for any refresh token, but you can assume that a refresh token will survive longer than the access token to which it is related. You can use a refresh token to request a new access token through the OAuth 2.0 token endpoint. You just need to submit an HTTP POST request with a body like the following:

grant_type=refresh_token&redirect_uri=[redirect_uri]&client_id=[ClientID]&client_secret=[ClientSecret]&refresh_token=[Refresh_Token]&resource= [target_resource_identifier]

 

In the previous HTTP body excerpt, the grant_type argument has a value of refresh_token, which means that we are granting the consumer’s identity through a refresh token, which is provided through the refresh_token argument, instead of by using an authorization code like in the previous examples. If the refresh token provided to the authorization server is expired or revoked, be ready to handle an Invalid Grant exception like the following:

AADSTS70002: Error validating credentials. AADSTS70008: The provided access grant is expired or revoked.

 

In that case, you will have to discard the refresh token and restart the handshake process, requesting a new authorization code through the OAuth 2.0 authorization endpoint.

 

Security flow summary

If you have read this section until this point, you know the inner workings of OpenID Connect authentication and OAuth 2.0 authorization. It should be clear that the entire process has to be secured using SSL and HTTPS; otherwise, any “man in the middle” could steal any of the authorization code, access token, or refresh token and be able to act on behalf of someone else.

 

That is the main reason for having all the modern web-based services provided over HTTPS instead of HTTP (think about Office 365, the Microsoft Graph API, the SharePoint Add-in model, Faceblog, Twitter, and so on). Whenever OAuth 2.0 and OpenID Connect are involved, the communication channel has to be encrypted.

 

In the next section, you will learn how to leverage these security protocols through high-level libraries and tools that will make the entire authentication and authorization process easy. Nevertheless, knowing how the entire flow works makes you more independent from any helper or utility library and allows you to debug or inspect what is happening if necessary.

 

Active Directory Authentication Library

If you read the previous section, you learned the inner workings of OAuth 2.0 and OpenID Connect within an application registered in Azure AD. However, since late 2013, Microsoft provides a high-level library, which is called Active Directory Authentication Library (ADAL). At the time of this writing, ADAL is available in version 3.10 and allows you to consume OAuth 2.0 and OpenID Connect easily with multiple flavors such as the following:

 

For further information about the available flavors of ADAL, you can read the document “Azure Active Directory Authentication Libraries,” which is available at the following URL: msdn.microsoft.com/en-us/library/azure/dn151135.aspx.

Microsoft .NET Client: .NET Client, Windows Store, Windows Phone (8.1)

Microsoft .NET Server: ASP.NET Site Open Web Interface, The Official Microsoft ASP.NET Site MVC

  • Java
  • JavaScript
  • OS X, iOS
  • Android
  • Node.js

The most interesting information about ADAL is that the various flavors are all available as open source projects on GitHub. Thus, you can leverage them as is, but you can also see their source code, contribute, and provide fixes and new capabilities if you like. In this last section, you will have a quick overview of ADAL and learn the basics about how to use ADAL in your own .NET projects. Using ADAL in an The Microsoft ASP.NET MVC web application

 

Assume that you have the The ASP.NET MVC application that you created in the previous topic

“Using Microsoft Visual Studio” in the section “Configuring apps and services in Azure AD.” In that case, you are using Microsoft Visual Studio 2015 and the Open Web Interface for .NET (OWIN) middleware to authenticate end users with Azure AD, thanks to OpenID Connect.

 

Supporting single tenancy with ADAL

It is now time to open the source code of the project and inspect the class file named Startup.Auth.cs, which is located under the App_Start folder of the web application.

 

The excerpt defines a single-tenant scenario in which the startup code initializes some useful variables that will store information like the ClientID and Client Secret from an OAuth 2.0 perspective, the target Tenant ID, and so on. The first point of interest is the UseOpenIdConnectAuthentication method invocation, which is highlighted in bold text and which engages the OWIN OpenID Connect middleware.

 

Inside the method invocation, the AuthorizationCodeReceived anonymous delegate implementation corresponds to the handling of the authorization code that we handled manually in the previous sections. The implementation uses the AuthenticationContext type, which is part of ADAL and is available in namespace Microsoft.IdentityModel.Clients.ActiveDirectory.

 

The AuthenticationContext type can be used to retrieve authentication tokens from Azure AD or ADFS 3.0. As you can see, the type constructor accepts a token cache object implementation, which is used to do token (access tokens and refresh tokens) caching based on the user ID.

 

Through the invocation of the AcquireTokenByAuthorizationCode method of the AuthenticationContext instance, the class retrieves an object of type AuthenticationResult, which holds information like the access token and a refresh token. These tokens will be cached in the token cache and will be used to consume the Microsoft Graph API of Azure AD for reading the profile information of the currently logged-in user.

 

By using the AuthenticationContext type, you can also retrieve access tokens for consuming other services like the Microsoft Graph API and all the other API that are published and secured through a Microsoft Azure AD tenant, for instance. The AuthenticationContext type offers methods like AquireToken, AcquireTokenByRefreshToken, AcquireTokenSilent, and others that can be used to request access tokens for consuming specific resources.

 

For example, if you want to acquire an access token to consume the Microsoft Graph API, you can use the following syntax:

  • String graphAPIResourceId = "Microsoft Graph";
  • var graphAPIAuthenticationResult = authContext.AcquireTokenSilent( graphAPIResourceId
  • credential,
  • UserIdentifier.AnyUser);
  • var accessTokenValue = graphAPIAuthenticationResult.AccessToken;

 

In the previous code excerpt, you will find in the accessTokenValue variable the value of the access token that you can inject as an Authorization Bearer header within any further REST request against the Microsoft Graph API endpoints.

 

Supporting multitenancy with ADAL

If you want to manage a multitenant solution, first you have to configure the app as multitenant in the Azure AD application configuration. You have to change the web.config file of the application slightly to support the common Tenant ID instead of a specific value. Thus, the ida:TenantId setting item will look like the following excerpt:

<add key="ida:TenantId" value="common" />

Just after that, you have to adapt the Startup.Auth.cs file to support a multitenancy scenario.

An excerpt of the Startup.Auth.cs file of a web application with OWIN and Cloud – Multiple Organization authentication configuration

public partial class Startup {
private static string clientId =
ConfigurationManager.AppSettings["ida:ClientId"]; private static string appKey =
ConfigurationManager.AppSettings["ida:ClientSecret"];
private static string aadInstance = ConfigurationManager.
AppSettings["ida:AADInstance"];
private static string tenantId =
ConfigurationManager.AppSettings["ida:TenantId"]; private static string postLogoutRedirectUri =
ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"]; public static readonly string Authority = aadInstance + tenantId;
This is the resource ID of the AAD Graph API. We'll need this to request
a token to call the Graph API.
string graphResourceId = "https://graph.windows.net";
public void ConfigureAuth(IAppBuilder app) { ApplicationDbContext db = new ApplicationDbContext();
app.SetDefaultSignInAsAuthenticationType(
CookieAuthenticationDefaults.AuthenticationType); app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
TokenValidationParameters = new System.IdentityModel.Tokens
.TokenValidationParameters {
instead of using the default validation
(validating against a single issuer value, as we do
in line of business apps),
we inject our own multitenant validation logic ValidateIssuer = false,
If the app needs access to the entire organization,
then add the logic of validating the Issuer here.
IssuerValidator
},
Notifications = new OpenIdConnectAuthenticationNotifications() {
AuthorizationCodeReceived = (context) => {
var code = context.Code;
ClientCredential credential = new ClientCredential( clientId, appKey);
string signedInUserID =
context.AuthenticationTicket.Identity.FindFirst(
ClaimTypes.NameIdentifier).Value;
string tenantID =
context.AuthenticationTicket.Identity.FindFirst(
"http://schemas.microsoft.com/identity/claims/tenant
.Value;
AuthenticationContext authContext = new
AuthenticationContext(
aadInstance + tenantID, new
ADALTokenCache(signedInUser
ID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Current.Request.Url
.GetLeftPart(UriPartial.Path)), credential,
graphResourceId);
return Task.FromResult(0);
}
}
});
}
}

The only difference is that we need to retrieve the Tenant ID from the claims of the currently logged-in user, if any. Then, the Tenant ID is used to create the AuthenticationContext instance that will be used to retrieve tokens. there is a custom configuration for the TokenValidationParameters property that allows you to inject a custom token issuer validation logic, if needed. In the code excerpt, there is a fake validation logic, but in your real business-level solutions you should validate token issuers carefully.

 

ADAL wrap-up

As you have just seen in practice, the ADAL library makes it easy to authenticate and to acquire access tokens and refresh tokens. Moreover, the ADAL library is able to renew access tokens automatically upon expiration by using the cached refresh token values.

 

Almost the same capabilities are available in all the other supported framework/technologies. Thus, feel free to leverage ADAL and all of its flavors in your real solutions to avoid doing a manual handshake with Azure AD and the OpenID Connect protocol.

 

Summary

In this blog, you learned about the architecture of Azure AD and the key role Azure AD plays in the architecture of Office 365. Moreover, you saw how you can leverage OAuth 2.0 and OpenID Connect to authenticate users and authorize access to the Microsoft Graph API on behalf of those users.

 

You also inspected how the OAuth 2.0 and OpenID Connect protocols work under the cover during these authentication and authorization processes. Last, you saw how to leverage the ADAL to interact with Azure AD and to manage authentication identities and authorization tokens in Microsoft .NET.

 

If you would like to walk through all the configuration and development steps illustrated in this blog, you can download the source code of the application that will be illustrated and

 

Mail, calendar, and contact services

This blog explains how to leverage the Microsoft Graph API services related to mail, calendar, and contacts. First, the blog illustrates how to set up your development environment to consume the Microsoft Graph API. Then, the blog covers the various flavors of available API in the fields of services related to Microsoft Exchange Online.

 

Setting up the environment

To develop a .NET solution that leverages the Microsoft Graph API, you need to create a new project, which can be almost any kind of project. It can be a console application, an ASP.NET MVC or Web Forms application, a WPF desktop application, and so on. Regardless of the kind of application you plan to develop, you will have to reference some .NET client libraries, and you will be able to play with REST and OData manually by using the HttpClient type of .NET.

 

The examples in this blog will be based on an ASP.NET MVC application, which can be, for example, a Microsoft Office 365 application. The user interface (UI) of the sample application is out of scope of this part of the book. The UI elements will mainly leverage the Office UI Fabric components, “Overview of Office 365 development,” “Creating Office 365 applications,” to provide a consistent UI and user experience (UX) to the end users of the application. In the code samples related to this book, which are on GitHub (SharePoint/PnP), you can see all the implementation details.

 

“Azure Active Directory and security,” to consume the Microsoft Graph API you need to register your application within the Microsoft Azure Active Directory (Azure AD) tenant, and you need to configure the application permissions properly. For further details about how to register an application in Azure AD.

 

The easiest way to create a project like the demo that you will see in the following paragraphs is to create a new ASP.NET web application, choose the ASP.NET MVC 4.x template, and configure the web application authentication to use Work And School Accounts. This way, your application will already be registered in the Azure AD tenant of your choice.

 

You will also need to install the Active Directory Authentication Library (ADAL) for .NET, which is available as a NuGet package with name “Microsoft.IdentityModel.Clients.ActiveDirectory.” At the time of this writing, the latest released version of ADAL is 3.x.

 

Once you have set up the project references and the NuGet packages, to consume any of the services available through the Microsoft Graph API you need to acquire an OAuth access token via ADAL, and to set up an HttpClient object that will consume the API by providing that specific OAuth access token within the HTTP headers of the request.

 

However, before you are able to acquire an access token through ADAL, you will need to customize the initialization code of the ASP.NET MVC project slightly. You will need to open the Startup.Auth.cs file, which is located under the App_Start folder of the ASP.NET 

 

MVC project, and add some logic to handle the OAuth 2.0 authorization flow. By default, configuring the application for Work And School Accounts authentication will set up an initial light version of that file, which looks like the code excerpt illustrated in Listing.

 

LISTING The out-of-box Startup.Auth.cs file in an ASP.NET project configured for Work And School Accounts authentication

public partial class Startup {
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
private static string authority = aadInstance + tenantId;
public void ConfigureAuth(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(
CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri });
}
}

 

These lines of code define the Open Web Interface for .NET (OWIN) pipeline and declare that the ASP.NET SMVC web application will use cookie-based authentication, followed by OpenID Connect authentication. The latter is also configured with a specific Client ID, Authority, and post logout redirect URL.

 

By default, all these custom configuration parameters are loaded from the web.config of the web application. In the code samples related to the current book part, these values are retrieved through a static class that shares all the general settings across the entire application. In Listing, you can see the revised version of the Startup.Auth.cs file, which includes the OAuth access token handling logic.

 

LISTING  The customized Startup.Auth.cs file in the sample ASP.NET project

public partial class Startup {
public void ConfigureAuth(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(
CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions { ClientId = MSGraphAPIDemoSettings.ClientId, Authority = MSGraphAPIDemoSettings.Authority,
PostLogoutRedirectUri = MSGraphAPIDemoSettings.PostLogoutRedirectUri, Notifications = new OpenIdConnectAuthenticationNotifications() {
SecurityTokenValidated = (context) => {
return Task.FromResult(0);
},
AuthorizationCodeReceived = (context) => { var code = context.Code;
ClientCredential credential = new ClientCredential(
MSGraphAPIDemoSettings.ClientId,
MSGraphAPIDemoSettings.ClientSecret);
string signedInUserID =
context.AuthenticationTicket.Identity.FindFirst(
ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext( MSGraphAPIDemoSettings.Authority,
new SessionADALCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code,
new
Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)),
credential,
MSGraphAPIDemoSettings.MicrosoftGraphResourceId);
return Task.FromResult(0);
},
AuthenticationFailed = (context) => { context.OwinContext.Response.Redirect("/Home/Error"); context.HandleResponse(); // Suppress the exception return Task.FromResult(0);
}
}
});
}
}

Within the OpenIdConnectAuthenticationOptions constructor, there is the configuration of the Notifications property, which is of type OpenIdConnectAuthenticationNotifications and allows you to intercept and handle some interesting events related to the OpenID authentication flow. In particular, the event called AuthorizationCodeReceived allows you to handle the OpenID Connect authorization code, and to request an OAuth access token and a refresh token based on that.

 

Inside the AuthorizationCodeReceived notification implementation, you can see the request for an access token through the invocation of method AcquireTokenByAuthorizationCode of an object of type AuthenticationContext. The result will be an object of type AuthenticationResult, which will include both an OAuth access token and a refresh token.

 

Notice also that the AuthenticationContext instance is created by providing an object of type SessionADALCache. This is a custom cache object that ADAL will use to cache both the access token and the refresh token for a single user, based on the user ID passed to the constructor of the cache object.

 

The session-based ADAL cache sample is simple, and in a real scenario you should use another kind of cache based, for example, on a SQL Server database and some Entity Framework code or even based on the Redis Cache of Microsoft Azure if your application will be hosted on Microsoft Azure.

 

Aside from the initialization code, which is executed whenever the user’s authentication flow starts, to access the Microsoft Graph API you will have to provide a valid OAuth access token. In Listing, you can see a helper function, which is part of the sample project, to retrieve an access token either from the ADAL cache or by refreshing a new one through the refresh token stored in the ADAL cache.

If neither the access token nor the refresh token is valid, the helper method will handle a full refresh of the authentication context by invoking the Challenge method of the current OWIN Authentication context.

 

LISTING A helper method to get an OAuth access token for accessing the Microsoft Graph API

<summary>
This helper method returns an OAuth Access Token for the current user
</summary>
<param name="resourceId">The resourceId for which we are requesting the token</param>
<returns>The OAuth Access Token value</returns>
public static String GetAccessTokenForCurrentUser(String resourceId = null) {
String accessToken = null;
if (String.IsNullOrEmpty(resourceId)) {
resourceId = MSGraphAPIDemoSettings.MicrosoftGraphResourceId;
}
try {
ClientCredential credential = new ClientCredential( MSGraphAPIDemoSettings.ClientId,
MSGraphAPIDemoSettings.ClientSecret);
string signedInUserID = System.Security.Claims.ClaimsPrincipal.Current
.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext( MSGraphAPIDemoSettings.Authority,
new SessionADALCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenSilent( MSGraphAPIDemoSettings.MicrosoftGraphResourceId, credential,
UserIdentifier.AnyUser);
accessToken = result.AccessToken;
}
catch (AdalException ex) {
if (ex.ErrorCode == "failed_to_acquire_token_silently") {
Refresh the access token from scratch
HttpContext.Current.GetOwinContext().Authentication.Challenge( new AuthenticationProperties {
RedirectUri = HttpContext.Current.Request.Url.ToString(),
},
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
else {
Rethrow the exception throw ex;
}
}
return (accessToken);
}

 

Notice the AcquireTokenSilent method invocation, which will get the access token from the ADAL cache or refresh it based on the cached refresh token. The ADAL cache, as described previously, is built based on the current user ID. In case of an exception with an error code with a value of failed_to_acquire_token_silently, the helper will invoke the Challenge method to start a new authentication flow, as discussed previously.

 

In Listing you can see a code excerpt that illustrates how to use the method GetAccessTokenForCurrentUser described in Listing.

LISTING  An excerpt of code that initializes an instance of the HttpClient leveraging the OAuth access token retrieved by invoking the GetAccessTokenForCurrentUser helper method

<summary>
This helper method makes an HTTP request and eventually returns a result
</summary>
<param name="httpMethod">The HTTP method for the request</param>
<param name="requestUrl">The URL of the request</param>
<param name="accept">The content type of the accepted response</param>
<param name="content">The content of the request</param>
<param name="contentType">The content type of the request</param>
<param name="resultPredicate">The predicate to retrieve the result, if any</param>
<typeparam name="TResult">The type of the result, if any</typeparam>
<returns>The value of the result, if any</returns>
private static TResult MakeHttpRequest<TResult>( String httpMethod,
String requestUrl,
String accept = null,
Object content = null,
String contentType = null,
Func<HttpResponseMessage, TResult> resultPredicate = null) {
Prepare the variable to hold the result, if any TResult result = default(TResult);
Get the OAuth Access Token
Uri requestUri = new Uri(requestUrl);
Uri graphUri = new Uri(MSGraphAPIDemoSettings.MicrosoftGraphResourceId); var accessToken =
GetAccessTokenForCurrentUser(requestUri.DnsSafeHost != graphUri.DnsSafeHost ?
$"{requestUri.Scheme}://{requestUri.Host}") : MSGraphAPIDemoSettings.MicrosoftGraphResourceId);
if (!String.IsNullOrEmpty(accessToken)) {
If we have the token, then handle the HTTP request HttpClient httpClient = new HttpClient();
Set the Authorization Bearer token httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
If there is an accept argument, set the corresponding HTTP header if (!String.IsNullOrEmpty(accept)) {
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue(accept));
}
Prepare the content of the request, if any
HttpContent requestContent =
(content != null) ?
new StringContent(JsonConvert.SerializeObject(content, Formatting.None,
new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new
CamelCasePropertyNamesContractResolver(),
}),
Encoding.UTF8, contentType) : null;
Prepare the HTTP request message with the proper HTTP method HttpRequestMessage request = new HttpRequestMessage(
new HttpMethod(httpMethod), requestUrl);
Set the request content, if any
if (requestContent != null) {
request.Content = requestContent;
}
// Fire the HTTP request
HttpResponseMessage response = httpClient.SendAsync(request).Result;
if (response.IsSuccessStatusCode) {
If the response is Success and there is a
predicate to retrieve the result, invoke it if (resultPredicate != null) {
result = resultPredicate(response);
}
}
else {
throw new ApplicationException(
String.Format("Exception while invoking endpoint {0}.",
graphRequestUri),
new HttpException(
(Int32)response.StatusCode,
response.Content.ReadAsStringAsync().Result));
}
}
return (result);
}

As you can see, the generic method called MakeHttpRequest internally handles any kind of HTTP request, based on the httpMethod and the graphRequestUri input arguments. Moreover, it sets up the HTTP Authorization header of type Bearer by using the value of the OAuth access token retrieved through the GetAccessTokenForCurrentUser method illustrated in Listing. The generic type TResult defines the type of the result, if any. In that case, the optional resultPredicate argument is used to retrieve a typed result for the HTTP request.

 

Notice the excerpt highlighted in bold, where the content of the HTTP request is defined. The syntax leverages the JsonConvert object, providing some custom serialization settings to suppress any null property that otherwise would be noisy for the target Microsoft Graph API.

 

The MakeHttpRequest method will be used internally by the MicrosoftGraphHelper class to fire any HTTP request in the following sections. In the MicrosoftGraphHelper class, there are a bunch of HTTP-related methods to make it easy to handle any kind of request. In Listing you can see the definition of these methods.

 

LISTING  Code excerpt of the HTTP-related methods defined in the MicrosoftGraphHelper class

<summary>
This helper method makes an HTTP GET request and returns the result as a String
</summary>
<param name="graphRequestUri">The URL of the request</param>
<returns>The String value of the result</returns>
public static String MakeGetRequestForString(String graphRequestUri) { return (MakeHttpRequest<String>("GET",
graphRequestUri,
resultPredicate: r => r.Content.ReadAsStringAsync().Result));
}
<summary>
This helper method makes an HTTP GET request and returns the result as a Stream
</summary>
<param name="graphRequestUri">The URL of the request</param>
<param name="accept">The accept header for the response</param>
<returns>The Stream of the result</returns>
public static http://System.IO.Stream MakeGetRequestForStream(String graphRequestUri, String accept) {
return (MakeHttpRequest<http://System.IO.Stream>("GET", graphRequestUri,
resultPredicate: r => r.Content.ReadAsStreamAsync().Result));
}
<summary>
This helper method makes an HTTP POST request without a response
</summary>
<param name="graphRequestUri">The URL of the request</param>
<param name="content">The content of the request</param>
<param name="contentType">The content/type of the request</param> public static void MakePostRequest(String graphRequestUri,
Object content = null, String contentType = null) {
MakeHttpRequest<String>("POST", graphRequestUri,
content: content,
contentType: contentType);
}
<summary>
This helper method makes an HTTP POST request and returns the result as a String
</summary>
<param name="graphRequestUri">The URL of the request</param>
<param name="content">The content of the request</param>
<param name="contentType">The content/type of the request</param>
<returns>The String value of the result</returns>
public static String MakePostRequestForString(String graphRequestUri, Object content = null,
String contentType = null) {
return (MakeHttpRequest<String>("POST",
graphRequestUri,
content: content,
contentType: contentType,
resultPredicate: r => r.Content.ReadAsStringAsync().Result));
}
<summary>
This helper method makes an HTTP PATCH request and returns the result as a String
</summary>
<param name="graphRequestUri">The URL of the request</param>
<param name="content">The content of the request</param>
<param name="contentType">The content/type of the request</param>
<returns>The String value of the result</returns>
public static String MakePatchRequestForString(String graphRequestUri, Object content = null,
String contentType = null) {
return (MakeHttpRequest<String>("PATCH",
graphRequestUri,
content: content,
contentType: contentType,
resultPredicate: r => r.Content.ReadAsStringAsync().Result));
}
<summary>
This helper method makes an HTTP DELETE request
</summary>
<param name="graphRequestUri">The URL of the request</param>
<returns>The String value of the result</returns>
public static void MakeDeleteRequest(String graphRequestUri) { MakeHttpRequest<String>("DELETE", graphRequestUri);
}
<summary>
This helper method makes an HTTP PUT request without a response
</summary>
<param name="requestUrl">The URL of the request</param>
<param name="content">The content of the request</param>
<param name="contentType">The content/type of the request</param> public static void MakePutRequest(String requestUrl,
Object content = null, String contentType = null) { MakeHttpRequest<String>("PUT",
requestUrl, content: content, contentType: contentType);
}
<summary>
This helper method makes an HTTP PUT request and returns the result as a String
</summary>
<param name="requestUrl">The URL of the request</param>
<param name="content">The content of the request</param>
<param name="contentType">The content/type of the request</param>
<returns>The String value of the result</returns>
public static String MakePutRequestForString(String requestUrl, Object content = null, String contentType = null) { return(MakeHttpRequest<String>("PUT",
requestUrl,
content: content,
contentType: contentType,
resultPredicate: r => r.Content.ReadAsStringAsync().Result));
}

You are now ready to consume the Microsoft Graph API within your code by leveraging these helper methods and the setup environment.

 

Mail services

“Microsoft Graph API reference,” to consume the mail services you will need to access the proper REST endpoint and process the related JSON responses. However, from a .NET perspective, you will have to deserialize every JSON response into something that can be handled by your code. To achieve this, you can, for example, use the Newtonsoft.Json package, which is available through NuGet.

 

Reading folders, messages, and attachments

When playing with the mail services, the first and most common thing to do is to enumerate the email messages that you have in a mailbox. In Listing, you can see a code excerpt about how to retrieve the list of mail folders in the current user’s mailbox.

 

LISTING  Code excerpt to enumerate the email folders in a mailbox

<summary>
This method retrieves the email folders of the current user
</summary>
<param name="startIndex">The startIndex (0 based) of the folders to retrieve</param>
<returns>A page of up to 10 email folders</returns>
public static List<MailFolder> ListFolders(Int32 startIndex = 0) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/mailFolders?$skip={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, startIndex));
var folders = JsonConvert.DeserializeObject<MailFolderList>(jsonResponse); return (folders.Folders);
}

 

As you can see, the method leverages the helper method MakeGetRequestForString. It uses the JsonConvert type of Newtonsoft.Json to convert the JSON response string into a custom type (MailFolderList) that holds a collection of objects of type MailFolder. 

 

Moreover, as you can see in the code highlighted in bold, the URL of the request handles paging of responses by leveraging the OData $skip query string parameter. By default, the Microsoft Graph API will return results in chunks of no more than 10 elements, skipping a number of elements based on the $skip parameter value. Thanks to this out-of-box behavior of the Microsoft Graph API, you easily can do paging of every result set with pages of up to 10 elements each.

 

LISTING Code excerpt that defines the MailFolderList and the MailFolder model types

<summary>
Defines a list of email message folders
</summary>
public class MailFolderList {
<summary>
The list of email message folders
</summary>
[JsonProperty("value")]
public List<MailFolder> Folders { get; set; }
}
<summary>
Defines an email Folder
</summary>
public class MailFolder : BaseModel {
<summary>
The display name of the email folder
</summary>
[JsonProperty("displayName")] public String Name { get; set; }
<summary>
Total number of items
</summary>
public Int32 TotalItemCount { get; set; }
<summary>
Number of unread items
</summary>
public Int32 UnreadItemCount { get; set; }
}

 

Notice that the MailFolder type provides just a subset of the properties defined in a mail folder, but the JsonConvert engine will handle that, including any property remapping, by leveraging the JsonProperty attribute. Shaping the MailFolder type and any other domain model type is a task you must perform based on your real business requirements if you want to consume the Microsoft Graph API manually and at low level, with pure HTTP, REST, and JSON.

 

Once you have the list of folders for the current user, you can access the email messages of a specific folder by making a REST request for a URL like the following: https://graph.microsoft.com/v1.0/me/mailFolders/<FolderID>/messages In Listing, you can see a code excerpt of a method that retrieves such a list of email messages.

 

LISTING Code excerpt of a method that retrieves the email messages of a mail folder

<summary>
This method retrieves the email messages from a folder in the current user's mailbox
</summary>
<param name="folderId">The ID of the target folder, optional</param>
<param name="startIndex">The startIndex (0 based) of the messages to retrieve</param>
<param name="includeAttachments">Defines whether to include attachments</param>
<returns>A page of up to 10 email messages in the folder</returns>
public static List<MailMessage> ListMessages(String folderId = null, Int32 startIndex = 0,
Boolean includeAttachments = false) {
String targetUrl = null;
if (!String.IsNullOrEmpty(folderId)) {
targetUrl = String.Format("{0}me/mailFolders/{1}/messages?$skip={2}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
folderId, startIndex);
}
else {
targetUrl = String.Format("{0}me/messages?$skip={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, startIndex);
}
String jsonResponse =
MicrosoftGraphHelper.MakeGetRequestForString(targetUrl);
var messages = JsonConvert.DeserializeObject<MailMessageList>(jsonResponse);
if (includeAttachments)
foreach (var message in messages.Messages.Where(m => m.HasAttachments))
{
message.LoadAttachments();
}
}
return (messages.Messages);
}

The logic of the ListMessages method illustrated in Listing is similar to that of the ListFolders method illustrated in Listing, including how the paging of results is handled. However, in the ListMessages method there is also some business logic to retrieve the attachments of the messages, if requested with the includeAttachments Boolean argument, by leveraging an extension method called LoadAttachments that extends the MailMessage custom type.

 

LISTING Code excerpt that defines the MailMessageList and the MailMessage types

<summary>
Defines a list of email messages
</summary>
public class MailMessageList {
<summary>
The list of messages
</summary>
[JsonProperty("value")]
public List<MailMessage> Messages { get; set; }
}
<summary>
Defines an email message
</summary>
public class MailMessage : BaseModel {
public MailMessage() {
this.Attachments = new List<MailAttachment>();
}
<summary>
The importance of the email message
</summary>
[JsonConverter(typeof(StringEnumConverter))] public ItemImportance Importance { get; set; }
<summary>
The sender email address
</summary>
[JsonProperty("from")
public MailMessageRecipient From { get; set; }
<summary>
The list of email address TO recipients
</summary>
[JsonProperty("toRecipients")]
public List<MailMessageRecipient> To { get; set; }
<summary>
The list of email address CC recipients
</summary>
[JsonProperty("ccRecipients")]
public List<MailMessageRecipient> CC { get; set; }
<summary>
The list of email address BCC recipients
</summary>
[JsonProperty("bccRecipients")]
public List<MailMessageRecipient> BCC { get; set; }
<summary>
The subject of the email message
</summary>
public String Subject { get; set; }
<summary>
The body of the email message
</summary>
public ItemBody Body { get; set; }
<summary>
The UTC sent date and time of the email message
</summary>
public Nullable<DateTime> SentDateTime { get; set; }
<summary>
The UTC received date and time of the email message
</summary>
public Nullable<DateTime> ReceivedDateTime { get; set; }
<summary>
Defines whether the email message is read on unread
</summary>
public Boolean IsRead { get; set; }
<summary>
Defines whether the email message is a draft
</summary>
public Boolean IsDraft { get; set; }
<summary>
Defines whether the email has attachments
</summary>
public Boolean HasAttachments { get; set; }
<summary>
The list of email message attachments, if any
</summary>
public List<MailAttachment> Attachments { get; private set; }
}
<summary>
Defines the importance of an email message
</summary>
public enum ItemImportance {
<summary>
Low importance
</summary>
Low,
<summary>
Normal importance, default value
</summary>
<summary>
High importance
</summary>
High,
}
<summary>
Defines a recipient of an email message/meeting
</summary>
public class UserInfoContainer {
<summary>
The email address of the recipient
</summary>
[JsonProperty("emailAddress")]
public EmailAddress Recipient { get; set; }
}
<summary>
Defines a user info
</summary>
public class UserInfo {
<summary>
The email address
</summary>
public String Address { get; set; }
<summary>
The description of the email address
</summary>
public String Name { get; set; }
}

 

The layouts of the custom MailMessage and MailMessageList types are defined to make it easier to deserialize the JSON response retrieved from the Microsoft Graph API into .NET complex types. You can also think about using some custom JsonConvert types to customize the out-of-box behavior of the Newtonsoft.Json library, transforming the results into something different from the JSON response structure.

 

For example, within the definition of the MailMessage type there is the custom converter of type StringEnumConverter applied to the Importance property of type ItemImportance, to serialize the value of the enum type as a JSON string instead of using a number.

 

Moreover, the properties called SentDateTime and ReceivedDateTime are of type Nullable<DateTime> to customize the behavior of the Newtonsoft.Json library while serializing an email message instance. These settings will be helpful later in this blog in the section “Sending an email message,” when we will send email messages through the Microsoft Graph API.

 

In Listing you can see how the LoadAttachments extension method is defined.

LISTING Code excerpt of the LoadAttachments extension method to retrieve the attachments of an email messag

<summary>
Extension method to load the attachments of an email message
</summary>
<param name="message">The target email message</param>
public static void LoadAttachments(this MailMessage message) {
if (message.HasAttachments) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/messages/{1}/attachments",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
http://message.Id));
var attachments = JsonConvert.DeserializeObject<MailAttachmentList> (jsonResponse);
message.Attachments.AddRange(attachments.Attachments);
foreach (var attachment in message.Attachments) { attachment.ParentMessageId = http://message.Id;
}
}
}

 

The business logic makes an HTTP request for the URL of the Microsoft Graph API that retrieves the attachments of the current message. Then, it deserializes the JSON response into a list of .NET complex types and loads each instance of the MailAttachment type into the collection of attachments of the target MailMessage instance. The binary content of every attachment is handled automatically by the Newtonsoft.Json library, and you will find it in the Byte array property with name Content of the custom type MailAttachment.

 

Unfortunately, if the email message for which you are downloading the attachments has one or more big files attached, executing the request illustrated in Listing could become expensive and slow, depending on the available bandwidth. It is better to leverage the OData querying capabilities and query for the list of attachments, including their size and excluding their binary content. Later, you can download just the necessary content of those attachments by accessing their URL endpoint directly. 

 

LISTING Code excerpt of the LoadAttachments extension method to retrieve the attachments of an email message, with improved code quality

<summary>
Extension method to load the attachments of an email message in a smart way
</summary>
<param name="message">The target email message</param>
public static void LoadAttachments(this MailMessage message) { if (message.HasAttachments) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/messages/{1}/attachments?
select=contentType,id,name,size",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
http://message.Id));
var attachments = JsonConvert.DeserializeObject<MailAttachmentList> (jsonResponse);
message.Attachments.AddRange(attachments.Attachments);
foreach (var attachment in message.Attachments) { attachment.ParentMessageId = http://message.Id;
}
}
}
<summary>
Extension method to load the content of a specific attachment
</summary>
<param name="message">The target email message</param>
public static void EnsureContent(this MailAttachment attachment) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/messages/{1}/attachments/{2}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, attachment.ParentMessageId, http://attachment.Id));
var result = JsonConvert.DeserializeObject<MailAttachment>(jsonResponse); attachment.Content = result.Content;
}

The EnsureContent method, takes care of downloading the binary content of a single attachment if it is needed.

 

Sending an email message

Another common use case is sending an email message through the Microsoft Graph API. Achieving this result with ASP.NET  MVC is straightforward. In Listing, there is a code excerpt to show how to leverage the microsoft.graph.sendMail action of the me entity.

 

LISTING Code excerpt showing how to send an email message

MailHelper.SendMessage(new Models.MailMessageToSend { Message = new Models.MailMessage {
Subject = "Test message",
Body = new Models.ItemBody {
Content = "<html><body><h1>Hello from ASP.NET MVC calling " + "the Microsoft Graph API!</h1></body></html>",
Type = Models.BodyType.Html,
},
To = new List<Models.UserInfoContainer>(new Models.UserInfoContainer[] { new Models.UserInfoContainer {
Recipient = new Models.UserInfo {
Name = "Thesis Scientist",
Address = "Thesis@Scientist.com",
}
}
}),
},
SaveToSentItems = true,
});
<summary>
This method sends an email message
</summary>
<param name="message"></param>
public static void SendMessage(MailMessageToSend message) { MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}me/microsoft.graph.sendMail", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri), message, "application/json");
}

 

In Listing, the content of the request is an object of type MailMessageToSend, which represents the structure of the JSON message expected by the microsoft.graph.sendMail method. The SendMessage method internally uses the helper method called MakePostRequest. It makes an HTTP POST request, providing the body content of the request as a JSON-serialized object.

 

As you can see from these examples, the processes for every kind of mail API call and every kind of complex type are similar. You have to make a REST request, with or without a request content, depending on the API you want to consume. You have to define a .NET complex type to hold the JSON serialization/deserialization of the request and the response.

 

Last, you have to do some custom mapping or additional REST requests to enrich the resulting object model. From an ASP.NET MVC application perspective, you also have to implement the controllers to handle the requests and the views to present the output to the end users.

 

Note

Implementing an  ASP.NET SMVC application is out of the scope of this book. If you need to improve your knowledge about how to create an ASP.NET  MVC application, you can read the web section “Learn About ASP.NET MVC,” which is available at the following URL: www.asp.net/mvc.

 

Reply, reply all, and forward email messages

Sending an email message is just one option you have. You can also reply to a received message, reply all, or forward a received message. To  accomplish these tasks you can rely on the actions reply, replyAll, and forward that are available through the Microsoft Graph API and apply to any object that represents an email message. In Listing, you can see a code excerpt of three helper methods that reply, reply all, and forward an email message.

 

LISTING Code excerpt to show the implementation of helper methods to reply, reply all, and forward an email message

<summary>
This method sends a reply to an email message
</summary>
<param name="messageId">The ID of the message to reply to</param>
<param name="comment">Any comment to include in the reply, optional</param> public static void Reply(String messageId, String comment = null) {
MicrosoftGraphHelper.MakePostRequest( String.Format("{0}me/messages/{1}/reply", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, messageId),
content: !String.IsNullOrEmpty(comment) ? new { Comment = comment } :
null,
contentType: "application/json");
}
<summary>
This method sends a reply all to an email message
</summary>
<param name="messageId">The ID of the message to reply all to</param>
<param name="comment">Any comment to include in the reply all, optional</param>
public static void ReplyAll(String messageId, String comment = null) { MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}me/messages/{1}/replyAll", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, messageId),
content: !String.IsNullOrEmpty(comment) ? new { Comment = comment } :
null,
contentType: "application/json");
}
<summary>
This method forwards an email message to someone else
</summary>
<param name="messageId">The ID of the message to forward</param>
<param name="recipients">The recipients of the forward</param>
<param name="comment">Any comment to include in the reply all, optional</param>
public static void Forward(String messageId,
List<UserInfoContainer> recipients, String comment = null) { MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}me/messages/{1}/forward", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, messageId), content: new {
Comment = !String.IsNullOrEmpty(comment) ? comment : null,
ToRecipients = recipients,
}, contentType: "application/json");
}

 

In the sample, the URL of the actions and the content of the reply/reply all messages that will be sent is highlighted in bold. Notice also the use of an anonymous type to hold the value of the response content. The same applies to the method for forwarding an email message, in which the HTTP request content is built as an anonymous type made of the Comment, which represents the body on top of the forward, and the ToRecipients, which are those to whom the message is forwarded. Using these methods to handle email responses is straightforward.

 

Calendar services

When developing custom Office 365 applications, it is often useful to interact with users’ calendars and events to provide functionalities around the basic calendar features. As with users’ mailboxes, here you will learn how to enumerate calendars and events and how to send, accept, and reject meeting requests by using C# within the ASP.NET Site MVC sample application.

 

Reading calendars and events

Browsing the list of a current user’s calendars—or anybody else’s calendars, as long as you have proper permissions—requires you to make an HTTP GET request for the calendars navigation property of the target user. In Listing, you can see the implementation of a helper method called ListCalendars that you can use to target the current user’s calendars.

 

LISTING Code excerpt to show the implementation of the ListCalendars method

<summary>
This method retrieves the calendars of the current user
</summary>
<param name="startIndex">The startIndex (0 based) of the folders to retrieve</param>
<returns>A page of up to 10 calendars</returns>
public static List<Calendar> ListCalendars(Int32 startIndex = 0) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/calendars?$skip={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, startIndex));
var calendarList = JsonConvert.DeserializeObject<CalendarList> (jsonResponse);
return (calendarList.Calendars);
}

The calendars navigation property supports OData querying and paging through the $skip query string argument. The result has to be deserialized into a typed object like the CalendarList type illustrated in Listing, which internally holds a collection of objects of type Calendar.

 

LISTING Code excerpt to show the definition of the CalendarList type

<summary>
Defines a list of calendars
</summary>
public class CalendarList {
<summary>
The list of calendars
</summary>
[JsonProperty("value")]
public List<Calendar> Calendars { get; set; }
}
<summary>
Defines a user's calendar
</summary>
public class Calendar : BaseModel {
<summary>
The color of the calendar
</summary>
public String Color { get; set; }
<summary>
The name of the calendar
</summary>
public String Name { get; set; }
}

Once you have the list of calendars, you can retrieve a single calendar object by ID by using a syntax like the one shown in Listing, where the GetCalendar helper method makes an HTTP GET request for a specific calendar item.

 

LISTING Code excerpt to show the GetCalendar method

<summary>
This method retrieves a calendar of the current user
</summary>
<param name="id">The ID of the calendar</param>
<returns>The calendar</returns>
public static Calendar GetCalendar(String id) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/calendars/{1}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, id));
var calendar = JsonConvert.DeserializeObject<Calendar>(jsonResponse); return (calendar);
}

The result of the HTTP request illustrated in Listing is a JSON string that can be deserialized into an object of custom type Calendar. To browse the events in the calendar, you will need to have a collection of objects of type Event, which can be retrieved by invoking the events navigation property of a calendar. The JSON response will be deserialized into the collection. In Listing, you can see a helper method to retrieve a calendar’s events with paging.

 

LISTING Code excerpt to show the definition of the ListEvents helper method

<summary>
This method retrieves the events of the current user's calendar
</summary>
<param name="calendarId">The ID of the calendar</param>
<param name="startIndex">The startIndex (0 based) of the items to retrieve</param>
<returns>A page of up to 10 events</returns>
public static List<Event> ListEvents(String calendarId, Int32 startIndex = 0) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/calendars/{1}/events?$skip={2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
calendarId,
startIndex));
var eventList = JsonConvert.DeserializeObject<EventList>(jsonResponse); return (eventList.Events);
}

 

An event is similar to an email message; the custom Event type shares many properties, which are highlighted in bold, with the MailMessage type. If you like, you could do some refactoring and share an abstract base class between the MailMessage and Event types.

 

Moreover, every Event type includes information specific to the event like the location, the start and end date, the event type and status, and so on. Most of these properties are defined through enumerations, and maybe you are wondering how to find the possible values for these enum types. The metadata document of the Microsoft Graph API provides you with all the needed information. If you browse to the metadata URL (https://graph.microsoft.com/v1.0/$metadata), at the beginning of the XML metadata document you will find the definition of all the enumerations and their possible values.

 

Browsing calendar views

As you probably noticed while playing with the code samples illustrated in the previous section, when you query the Microsoft Graph API for the events of a calendar, you get back up to 10 current and past events. You probably will also need to play with events in the future. 

 

LISTING Code excerpt that shows how to retrieve the events within a specific date range

<summary>
Retrieves the events of the current user's calendar within a specific date range
</summary>
<param name="calendarId">The ID of the calendar</param>
<param name="startDate">The start date of the range</param>
<param name="endDate">The end date of the range</param>
<param name="startIndex">The startIndex (0 based) of the items to retrieve</param>
<returns>A page of up to 10 events</returns>
public static List<Event> ListEvents(String calendarId, DateTime startDate, DateTime endDate, Int32 startIndex = 0) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/calendars/{1}/calendarView?" + "startDateTime={2:o}&endDateTime={3:o}&$skip={4}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, calendarId,
startDate.ToUniversalTime(),
endDate.ToUniversalTime(),
startIndex));
var eventList = JsonConvert.DeserializeObject<EventList>(jsonResponse); return (eventList.Events);
}

The URL of the request targets the calendarView navigation property of the target calendar object and is built providing the arguments startDateTime and endDateTime within the query string. Notice that the values of the two date and time arguments are converted into UTC time zone and formatted according to the OData protocol format requirements.

 

You will still get back objects of type Event, divided in pages of no more than 10 items. Thus, if you want to download all the events or multiple pages of events, you will still have to make multiple requests, leveraging the $skip query string argument. Keep in mind that you are not obliged to download the entire set of properties for every event. You can use the $select query string argument introduced earlier to project a subset of the properties to download only what you need.

 

Managing series of events

Another interesting scenario is recurring events and series of events. From a business requirements perspective, you may need to create a custom Office 365 application that enriches the capabilities around an event series. As you have seen in the previous section, every event has a Type property.

 

When that property assumes a value of SeriesMaster, Occurrence, or Exception, it means that the event is part of a series. Aside from the event of type SeriesMaster, which is the main event of a series as the type implies, the two other kinds of events have a property called SeriesMasterId, which is a reference to the ID of the master event for the series of events.

 

The events of type Occurrence are regular events based on a series, while the events of type Exception correspond to modified items of a series. Moreover, in every recurring event there is a complex property called Recurrence that defines the recurrence pattern and range for the series of events. 

 

LISTING

Code excerpt showing the definition of the EventRecurrence type
<summary>
Defines the Recurrence for a series of events
</summary>
public class EventRecurrence {
<summary>
The Recurrence Pattern
</summary>
public EventRecurrencePattern Pattern { get; set; }
<summary>
The Recurrence Range
</summary>
public EventRecurrenceRange Range { get; set; }
}
<summary>
Defines the Recurrence Pattern for a series of events
</summary>
public class EventRecurrencePattern {
<summary>
The day of the month for the recurrence
</summary>
public Int32 DayOfMonth { get; set; }
<summary>
The days of the week for the recurrence
</summary>
[JsonProperty(ItemConverterType = typeof(StringEnumConverter))] public DayOfWeek[] DaysOfWeek { get; set; }
<summary>
The first day of the week
</summary>
[JsonConverter(typeof(StringEnumConverter))]
public DayOfWeek FirstDayOfWeek { get; set; }
<summary>
The week of the month
</summary>
[JsonConverter(typeof(StringEnumConverter))] public WeekIndex Index { get; set; }
<summary>
The interval for repeating occurrences
</summary>
public Int32 Interval { get; set; }
<summary>
The month for the recurrence
</summary>
public Int32 Month { get; set; }
<summary>
The type of recurrence
</summary>
[JsonConverter(typeof(StringEnumConverter))] public RecurrenceType Type { get; set; }
}
<summary>
Defines the Recurrence Range for a series of events
</summary>
public class EventRecurrenceRange
{
<summary>
The Start Date of the recurrence
</summary>
[JsonConverter(typeof(Converters.DateOnlyConverter))] public Nullable<DateTime> StartDate { get; set; }
<summary>

The End Date of the recurrence

</summary>
[JsonConverter(typeof(Converters.DateOnlyConverter))] public Nullable<DateTime> EndDate { get; set; }
<summary>
The number of occurrences
</summary>
public Int32 NumberOfOccurrences { get; set; }
<summary>
The reference TimeZone for the recurrence
</summary>
public String RecurrenceTimeZone { get; set; }
<summary>
The type of recurrence
</summary>
[JsonConverter(typeof(StringEnumConverter))] public RecurrenceRangeType Type { get; set; }
}

 

There are some infrastructural enumerations under the cover of some properties. However, for the sake of simplicity, they are not illustrated in the code excerpt of Listing 5-20. You can also see that some of the properties are decorated with custom attributes to adapt the Newtonsoft.Json serialization engine to the context.

 

For example, the StartDate and EndDate properties of the EventRecurrenceRange type have a custom JSON converter to serialize date-only values, skipping the time part. In addition, the DaysOfWeek property of the EventRecurrencePattern type has a JsonProperty attribute to instruct the serialization engine to handle every item of the array with the custom StringEnumConverter converter provided by the Newtonsoft.Json library.

 

If you want to retrieve all the occurrences of a specific series of events, you can leverage the instances navigation property of the event that is master of the series. To invoke the instances navigation property, you have to provide a startDateTime and an endDateTime query string parameter to declare the boundaries of the range of dates from which you want to retrieve the instances.

 

You cannot retrieve all the instances at once; the events will be divided into chunks of up to 10 items, and you can leverage the $skip query string argument to move across pages of instances. In Listing, you can see a code excerpt to retrieve the event occurrences of an event series based on the ID of the master event.

 

LISTING Code excerpt to retrieve the events of a series of events from a target calendar

<summary>
This method retrieves the events of a series within a specific date range
</summary>
<param name="calendarId">The ID of the calendar</param>
<param name="masterSeriesId">The ID of the master event of the series</param>
<param name="startDate">The start date of the range</param>
<param name="endDate">The end date of the range</param>
<param name="startIndex">The startIndex (0 based) of the items to retrieve</param>
<returns>A page of up to 10 events</returns>
public static List<Event> ListSeriesInstances(String calendarId, String masterSeriesId,
DateTime startDate,
DateTime endDate,
Int32 startIndex = 0) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/calendars/{1}/events/{2}/instances?" + "startDateTime={3:o}&endDateTime={4:o}&$skip={5}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
calendarId,
masterSeriesId,
startDate.ToUniversalTime(),
endDate.ToUniversalTime(),
startIndex));
var eventList = JsonConvert.DeserializeObject<EventList>(jsonResponse); return (eventList.Events);
}

 

The helper method builds the URL of the request, setting the ID of the calendar and of the master event of the series. Moreover, it configures the startDateTime, endDateTime, and $skip query string arguments.

 

Creating and updating events

Now let’s say you want to create an event, which can be a single instance event or an event series. You will need to create a typed object that can be serialized into a JSON representation of the event type expected by the Microsoft Graph API. Then, you will have to send it through an HTTP POST request, targeting the URL endpoint of the collection of events where you want to create the new item. In Listing , you can see an example of how to create a single instance event.

 

LISTING Code excerpt to create a new single instance event in a target calendar

var singleEvent = CalendarHelper.CreateEvent(calendars[0].Id, new Models.Event {
Attendees = new List<Models.UserInfoContainer>( new Models.UserInfoContainer[] {
new Models.UserInfoContainer {
Recipient = new Models.UserInfo {
Address = "aca@pi.com",
Name = "Thesis Scientist",
}
},
new Models.UserInfoContainer {
Recipient = new Models.UserInfo {
Address = "someone@company.com",
Name = "Someone Else",
}
},
}),
Start = new Models.TimeInfo {
DateTime = DateTime.Now.AddDays(2).ToUniversalTime(), TimeZone = "UTC"
},
OriginalStartTimeZone = "UTC",
End = new Models.TimeInfo {
DateTime = DateTime.Now.AddDays(2).AddHours(1).ToUniversalTime(), TimeZone = "UTC"
},
OriginalEndTimeZone = "UTC",
Importance = Models.ItemImportance.High, Subject = "Introducing the Microsoft Graph API", Body = new Models.ItemBody {
Content = "<html><body><h2>Let's talk about the " + "Microsoft Graph API!</h2></body></html>",
Type = Models.BodyType.Html,
},
Location = new Models.EventLocation {
Name = "PiaSys.com Head Quarters",
},
IsAllDay = false,
IsOrganizer = true,
ShowAs = Models.EventStatus.WorkingElsewhere, Type = Models.EventType.SingleInstance,
});

 

As you can see, the excerpt creates an instance of an object of type Event and submits it to the CreateEvent helper method of the CalendarHelper class. Notice that the event is a meeting request because the Attendees property is configured to contain a couple of people. The Microsoft Graph API will take care of sending all the invitations for you. The result of this HTTP method invocation will be the just-created instance of the event, so you don’t need to make another query to retrieve it.

 

If you want to create an event series, you can submit an Event instance configured accordingly, providing a recurrence pattern and a range for the occurrences. In Listing 5-23, you can see an example to create an event series that recurs until the end of the current month, every Monday from 9.00 A.M. to 10.00 A.M.

 

LISTING Code excerpt to create an event series in a target calendar

var eventSeries = CalendarHelper.CreateEvent(calendars[0].Id, new Models.Event {
Start = new Models.TimeInfo {
DateTime = nextMonday9AM.ToUniversalTime(), TimeZone = "UTC"
},
OriginalStartTimeZone = "UTC",
End = new Models.TimeInfo {
DateTime = nextMonday9AM.AddHours(1).ToUniversalTime(), TimeZone = "UTC"
},
OriginalEndTimeZone = "UTC",
Importance = Models.ItemImportance.Normal,
Subject = "Recurring Event about Microsoft Graph API", Body = new Models.ItemBody {
Content = "<html><body><h2>Let's talk about the " + "Microsoft Graph API!</h2></body></html>",
Type = Models.BodyType.Html,
},
Location = new Models.EventLocation {
Name = "Thesis's Office",
},
IsAllDay = false,
IsOrganizer = true,
ShowAs = Models.EventStatus.Busy,
Type = Models.EventType.SeriesMaster,
Recurrence = new Models.EventRecurrence {
Pattern = new Models.EventRecurrencePattern {
Type = Models.RecurrenceType.Weekly,
DaysOfWeek = new DayOfWeek[] { DayOfWeek.Monday },
Interval = 1,
},
Range = new Models.EventRecurrenceRange { StartDate = nextMonday9AM.ToUniversalTime(),
Type = Models.RecurrenceRangeType.EndDate, EndDate = lastDayOfMonth.ToUniversalTime(),
}
}
});

It is clear that the only differences between a single instance event and a series of events are the configuration of the Recurrence property and the value of the Type property, which has a value of SeriesMaster. Aside from that, the just-created event is like any other object of type Event. Under the cover, the Microsoft Graph API will create for you all the series of events in the target calendar and will send the invitations if the recurring event is a meeting request.

 

Now, suppose you want to change one event in the series or another event that you have in a calendar. To accomplish this, you will need to get the event instance from the calendar, apply any changes, and resubmit the event to the calendar by using an HTTP PATCH method.

 

LISTING Code excerpt of a helper method to retrieve a single event from a target calendar

<summary>
This method retrieves an event from a calendar
</summary>
<param name="calendarId">The ID of the calendar</param>
<param name="eventId">The ID of the event</param>
<returns>The retrieved event</returns>
public static Event GetEvent(String calendarId, String eventId) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/calendars/{1}/events/{2}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, calendarId, eventId));
var calendarEvent = JsonConvert.DeserializeObject<Event>(jsonResponse); return (calendarEvent);
}

In Listing  you can see the definition of a helper method to update an event, leveraging the HTTP PATCH method.

 

LISTING  Code excerpt of a helper method to update a single event in a target calendar

<summary>
This method updates an event in a calendar
</summary>
<param name="calendarId">The ID of the calendar</param>
<param name="eventId">The event to update</param>
<returns>The updated event</returns>
public static Event UpdateEvent(String calendarId, Event eventToUpdate) {
String jsonResponse = MicrosoftGraphHelper.MakePatchRequestForString( String.Format("{0}me/calendars/{1}/events/{2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, calendarId, http://eventToUpdate.Id),
eventToUpdate, "application/json");
var updatedEvent = JsonConvert.DeserializeObject<Event>(jsonResponse); return (updatedEvent);
}

 

Notice that the Microsoft Graph API returns the updated event as the result of the HTTP PATCH method. This behavior is useful if you want to refresh the event view in your business logic because you don’t have to make another request to refresh your local copy of the updated event. If you want to delete an event from a calendar, you can make an HTTP DELETE request against the URL of the event to remove. In Listing you can see a code excerpt of the helper method to do that.

 

LISTING Code excerpt of a helper method to delete a single event from a target calendar

<summary>
This method deletes an event from a calendar
</summary>
<param name="calendarId">The ID of the calendar</param>
<param name="eventId">The ID of the event to delete</param>
public static void DeleteEvent(String calendarId, String eventId) {
MicrosoftGraphHelper.MakeDeleteRequest( String.Format("{0}me/calendars/{1}/events/{2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, calendarId, eventId));
}

Again, the powerful Microsoft Graph API will send for you a cancellation email if the event deleted is a meeting request that involves someone else.

 

Managing invitations for meeting requests

What if you get an invitation for a meeting request? Let’s say you want to write a custom Office 365 application to automate the process of handling meeting request invitations. You can use the HTTP helper methods that we have to send feedbacks for meeting requests. In blog 3, you saw that every meeting request has the ResponseStatus property and that you can leverage the operations Accept, Decline, and TentativelyAccept to provide your feedback to the meeting organizer. In Listing, you can see a code excerpt to give a feedback for a received meeting request.

 

LISTING Code excerpt of a helper method to provide a feedback for a received meeting request

<summary>
This method provides a feedback for a received meeting request
</summary>
<param name="calendarId">The ID of the calendar</param>
<param name="eventId">The ID of the meeting request</param>
<param name="feedback">The feedback for the meeting request</param <param name="comment">Any comment to include in the feedback, optional</param>
public static void SendFeedbackForMeetingRequest(String calendarId,
String eventId,
MeetingRequestFeedback feedback,
String comment = null) {
MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}me/calendars/{1}/events/{2}/{3}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, calendarId, eventId, feedback),
content: !String.IsNullOrEmpty(comment) ? new { Comment = comment
} : null,
contentType: "application/json");
}

Note that the feedback is represented using a custom enumeration of possible feedbacks, which is called MeetingRequestFeedback. Moreover, every meeting request feedback can include a comment that will be sent to the meeting organizer with the feedback. Thus, the custom helper method accepts a comment argument of type String, which is provided within the HTTP request content using an anonymous type that will be serialized in JSON according to the content structure expected by the backing Microsoft Graph API.

 

Contact services

The last group of services provided by the Microsoft Graph API against Microsoft Exchange Online are those related to handling contacts. In this section, you will learn how to retrieve, add, update, or delete users’ personal contacts.

 

Reading contacts

Like mailboxes and calendars, you can access a user’s default folder of contacts by making a request for the contacts navigation property of a user, whether it is the current user (that is, me) or any other user for whom you have the permission to read contacts. For example, in Listing there is a helper method to retrieve the contacts of the current user.

 

LISTING Code excerpt of a helper method to get the contacts of the current user

<summary>
This method retrieves the contacts of the current user
</summary>
<param name="startIndex">The startIndex (0 based) of the contacts to retrieve</param>
/// <returns>A page of up to 10 contacts</returns>
public static List<Contact> ListContacts(Int32 startIndex = 0) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/contacts?$skip={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, startIndex));
var contactList = JsonConvert.DeserializeObject<ContactList>(jsonResponse); return (contactList.Contacts);
}

As expected, the method retrieves contacts in pages of up to 10 items each. There is nothing special in the code sample of Listing  when compared to those related to email messages, calendars, or events. The only interesting difference is the outline of the Contact type, which presents some of the main properties returned by the Microsoft Graph API for every contact object. 

 

LISTING Code excerpt to define the Contact and ContactList types

<summary>
Defines a list of contacts
</summary>
public class ContactList {
<summary>
The list of contacts
</summary>
[JsonProperty("value")]
public List<Contact> Contacts { get; set; }
}
<summary>
Defines a user's contact
</summary>
public class Contact : BaseModel {
<summary>
The business address of the contact
</summary>
public PhysicalAddress BusinessAddress { get; set; }
<summary>
The business phones of the contact
</summary>
public List<String> BusinessPhones { get; set; }
<summary>
The business home page of the contact
</summary>
public String BusinessHomePage { get; set; }
<summary>
The company name of the contact
</summary>
public String CompanyName { get; set; }
<summary>
The department name of the contact
</summary>
public String Department { get; set; }
<summary The display name of the contact </summary>
public String DisplayName { get; set; }
<summary>
The list of email addresses of the contact
</summary>
public List<UserInfo> EmailAddresses { get; set; }
<summary>
The "File As" of the contact
</summary>
public String FileAs { get; set; }
<summary>
The home address of the contact
</summary>
public PhysicalAddress HomeAddress { get; set; }
<summary>
The home phones of the contact
</summary>
public List<String> HomePhones { get; set; }
<summary>
The office location of the contact
</summary>
public String OfficeLocation { get; set; }
<summary>
The other address of the contact
</summary>
public PhysicalAddress OtherAddress { get; set; }
<summary>
Personal notes about the contact
</summary>
public String PersonalNotes { get; set; }
<summary>
The first name of the contac
</summary>
public String GivenName { get; set; }
<summary>
The family name of the contac
</summary>
public String Surname { get; set; }
<summary>
The title of the contact
</summary>
public String Title { get; set; }
}
<summary>
Defines a physical address
</summary>
public class PhysicalAddress {
<summary>
The Street part of the address
</summary>
public String Street { get; set; }
<summary>
The City part of the address
</summary>
public String City { get; set; }
<summary>
The State part of the address
</summary>
public String State { get; set; }
<summary>
The Country or Region part of the address
</summary>
public String CountryOrRegion { get; set; }
<summary>
The Postal Code part of the address
</summary>
public String PostalCode { get; set; }
}

You can define these types as you like, as long as they can be used to deserialize the JSON response returned by the service. When looking at how the code samples related to this book implement these types, it is interesting to notice how the properties HomeAddress, BusinessAddress, and OtherAddress are defined by leveraging the PhysicalAddress type.

 

Moreover, note that the property EmailAddresses of every contact is a collection of instances of type UserInfo, which is also used to define the recipients of an email message. Thus, you can reuse this property to send an email message directly to a specific contact.

 

Furthermore, like with email messages and calendars, with contacts you can browse multiple contact folders. Every user object has a navigation property called contactFolders that represents a container folder for contacts. By default, every user’s account has a default contact folder that is the root folder, which can contain a hierarchy of child folders. Using the navigation properties contactFolders and childFolders, you can browse all these folders. 

 

LISTING Code excerpt to retrieve the contacts of a contacts folder

<summary>
This method retrieves the contacts of a contacts folder for the current user
</summary>
<param name="contactFolderId">The ID of the contacts folder</param>
<param name="startIndex">The startIndex (0 based) of the contacts to retrieve</param>
<returns>A page of up to 10 contacts</returns>
public static List<Contact> ListContacts(String contactFolderId, Int32 startIndex = 0) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/contactFolders/{1}/contacts?$skip={2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
contactFolderId,
startIndex));
var contactList = JsonConvert.DeserializeObject<ContactList>(jsonResponse); return (contactList.Contacts);
}

 

Every contact can have a picture to better define the contact in the address list. To access the contact’s picture, as you will do in blog 6, you can make an HTTP GET request for the navigation property photo and extract its $value. 

 

LISTING Code excerpt of a helper method to get a contact’s picture

<summary>
Retrieves the picture of a contact, if any
</summary>
<param name="contactId">The ID of the contact</param>
<returns>The picture as a binary Stream</returns> public static Stream GetContactPhoto(String contactId) {
Stream result = null;
String contentType = "image/png";
try {
result = MicrosoftGraphHelper.MakeGetRequestForStream( String.Format("{0}me/contacts/{1}/photo/$value",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, contactId), contentType);
}
catch (HttpException ex) {
if (ex.ErrorCode == 404) {
If 404 -> The contact does not have a picture
Keep NULL value for result
result = null;
}
}
return (result);
}

When you request the photo of a contact that does not have an associated image, the Microsoft Graph API will return an HTTP Status Code with a value of 404 (Not Found) and an error message with a value of “The specified object was not found in the store.” 

 

Managing contacts

Another set of common tasks when you work with contacts is updating existing contacts and adding new contacts. As you can imagine, updating an existing contact is just a matter of making an HTTP PATCH request, providing the JSON serialized representation of the contact to update. The Contact type defined in Listing is also suitable for this task. 

 

LISTING Code excerpt of a helper method to update a contact

<summary>
This method updates a contact
</summary>
<param name="contact">The contact to update</param>
<returns>The updated contact</returns>
public static Contact UpdateContact(Contact contact) {
String jsonResponse = MicrosoftGraphHelper.MakePatchRequestForString( String.Format("{0}me/contacts/{1}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
http://contact.Id),
contact,
"application/json");
var updatedContact = JsonConvert.DeserializeObject<Contact>(jsonResponse); return (updatedContact);
}

In Listing, you can see how to add a new contact.

 

LISTING Code excerpt of a helper method to add a new contact

<summary>
This method adds a contact
</summary>
<param name="contact">The contact to add</param>
<returns>The added contact</returns>
public static Contact AddContact(Contact contact) {
String jsonResponse = MicrosoftGraphHelper.MakePostRequestForString( String.Format("{0}me/contacts",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri),
contact,
"application/json");
var addedContact = JsonConvert.DeserializeObject<Contact>(jsonResponse); return (addedContact);
}

Both the methods return the updated or added contact object to make it easier for you to handle the refresh of the UI, if needed. Deleting a contact is also straightforward. In Listing you can see the corresponding helper method, which accepts the ID of the contact to delete.

 

LISTING Code excerpt of a helper method to delete a contact

<summary>
This method deletes a contact
</summary>
<param name="contactId">The ID of the contact to delete</param> public static void DeleteContact(String contactId) {
MicrosoftGraphHelper.MakeDeleteRequest( String.Format("{0}me/contacts/{1}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri contactId));
}

 

Summary

In this blog, you saw how to set up an ASP.NET MVC application to authenticate with Microsoft Azure Active Directory and how to register the application in the Azure AD tenant that sits under the cover of a Microsoft Office 365 tenant. Moreover, you saw how to leverage the Microsoft Active Directory Authentication Library (ADAL) to acquire an OAuth access token and how to store that token and a refresh token in a temporary cache.

 

Next, you saw how to leverage a helper class to manage the OAuth access token and make all the required HTTP requests to consume the Microsoft Graph API.

 

Last, you saw how to build a client application on top of these foundations that can consume the mail, calendar, and contact services provided by the Microsoft Graph API. You can use the code you saw in this blog and the related code samples you can find on the Internet ((SharePoint/PnP)) as a starting point for writing you own software solutions that leverage the powerful capabilities of the Microsoft Graph.

Recommend