Building an MS Outlook Add-In: Part Two – Building an Add-In with the Microsoft Graph API

Welcome back to part two of our series, where we’re aiming to be able to save an email (as an EML file) from a user’s inbox into a CRM system (or any other system for that matter!) via an Outlook add-in.

This is the second of a three-part series; you can find parts one and three below.

In this part, we’re looking at working with the Graph API, accessing your email content and connecting with your CRM.

Managing Access to the Graph API

For security, for your system to access the Graph API, your users must grant your add-in access to their Graph API resources on their behalf. This is a complicated and fiddly process! Learn how to register an Office add-in that uses single sign-on with Microsoft.

You must get up to speed on Microsoft Azure, App Registrations, and Enterprise Applications to get all this working!

Getting your scopes in order is key to getting this working. Scopes are required for accessing different Graph API resources.

As an example, let’s say that we have read the documentation and we want these scopes:

  • email
  • openid
  • profile
  • offline_access
  • User.Read
  • Mail.Read
  • Files.ReadWrite

This is a reasonable minimum set of scopes for copying emails from Microsoft 365/Outlook into your CRM system.

The main challenge here is copying these scopes into the right places in your tech stack. To summarise, these places are:

PlaceScopes To Copy In
Your Add-in Manifestopenid
profile
offline_access
User.Read
Mail.Read
Files.ReadWrite
Your App Registration in AzureEmail
openid
profile
offline_access
User.Read
Mail.Read
Files.ReadWrite
In Your Add-in CodeUser.Read
Mail.Read
Files.ReadWrite
In Your Server-Side Code When Performing Code ExchangeUser.Read
Mail.Read
Files.ReadWrite
Offline_access

Your add-in manifest will need a section like this:

<WebApplicationInfo>
            <Id>446d4af8-a41c-4732-9a7d-b1d9752a63c2</Id>
            <Resource>api://localhost:3300/446d4af8-a41c-4732-9a7d-b1d9752a63c2</Resource>
            <Scopes>
                <Scope>openid</Scope>
                <Scope>offline_access</Scope>
                <Scope>profile</Scope>
                <Scope>user.read</Scope>
                <Scope>mail.read</Scope>
                <Scope>mail.readwrite</Scope>
                <Scope>files.readwrite</Scope>
                <Scope>mailboxsettings.readwrite</Scope>
            </Scopes>
        </WebApplicationInfo>

Learn how to set up your app registration in Azure.

Your add-in code will need updating, assuming you have generated it using Yeoman.

const clientId = “446d4af8-a41c-4732-9a7d-b1d9752a63c2”;
const apiPrefix = “api://localhost:3000”;
const accessScope = `api://${apiPrefix}/${clientId}/access_as_user`;
const loginRequest = {
    scopes: [accessScope],
    extraScopesToConsent: [
        'user.read',
        'mail.read',
        'mail.readwrite',
        'files.readwrite',
    ],
};

Finally, you must update your server-side code that implements the “on behalf of” flow.

Alternatively, you can manually implement the “on behalf of” flow (our recommended approach).

Authenticate with Microsoft Graph API

Implement authentication using OAuth 2.0 to connect your add-in to the Microsoft Graph API. This step ensures secure access to the user’s mailbox and associated data.

If you have correctly implemented the complex OAuth system described above, your server-side code should be able to access the Graph API on behalf of a user. By this point, you have probably spotted that your user must log in to two different systems when they first start your add-in! These are:

  1. Your core CRM system
  2. Graph API (using their Azure AD / Entra ID / Microsoft account)

The good news is that in most modern circumstances, the login to Graph API will be almost invisible and managed by the SSO mechanism you set up earlier. Some users will use older Outlook email clients, while others will be on M365 tenants that are not configured for Modern Authentication. For these, you should implement fallback authentication as required by the Microsoft documentation.

Convert a Rest API ID to an Immutable Graph API Id

We are almost ready to retrieve our email content, but not quite! We must now clear the hurdle of getting your email ID in order.

The ID of the email provided by the add-in is not ideal for Graph API! You get the ID of the email in your JavaScript code like this:

office.context.mailbox.item.itemId

The ID returned by this process is invalid for Graph API as it has been encoded using a slightly different Base64 mechanism. To make it valid for use with Graph API, you can either fix it yourself or use Office’s function:

Office.context.mailbox.convertToRestId(…)

We are still not 100% done with the ID… The Id that you receive from the Outlook add-in is not an immutable ID. This means that if a user moves the email between folders in Outlook (for example), a new ID will be generated for this email.

To combat this problem, there are two steps you should take in your server-side code:

  1. Tell Graph API to use only immutable IDs
  2. Convert the Outlook ID to an immutable ID

Telling Graph API to use only immutable IDs involves adding a header to every request. If you’re using the recommended C# SDK, the easiest way to do this is when you create the client:

HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Prefer", "IdType=\"ImmutableId\"");
return new GraphServiceClient(client);

To convert the Outlook transient ID to an immutable API, you would use the handy Graph API provided by Microsoft:

await graphClient.Me.TranslateExchangeIds.PostAsync(
new TranslateExchangeIdsPostRequestBody {
		InputIds = new List<string> { graphEmailId },
		TargetIdType = ExchangeIdFormat.RestImmutableEntryId,
		SourceIdType = ExchangeIdFormat.RestId
	});

Access Email Content

Utilise the Microsoft Graph API to retrieve the content of the currently open email. Extract relevant information such as sender details, subject, and body text.

This is the easy bit. To copy the email (e.g. as an .eml file) to your CRM system, we only have one hoop to jump through! You must make an API call like this:

await graphClient.Me.Messages[messageId].Content.GetAsync());

The ”messageId” used here is the immutable ID explained above. This API call will return a stream. The stream contains the raw bytes of the MIME content of the email (effectively, the .eml file that we are looking for).

Connect to CRM System

Integrate with the CRM system using its API. Retrieve access tokens or API keys for authentication and establish a connection to enable data transfer. This is your system. You know what you are doing! Consider how your user will log into the add-in. This can be complex if you have not implemented OAuth in your core CRM system.

Our only special note is that Safari (and the associated WebView control) on MacOS has a very aggressive approach to security and third-party cookies. This is called “Intelligent Tracking Prevention” by Apple. As an add-in is presented as running in an MS Office iFrame in web clients, it can be very difficult to get authentication to work if your authentication system relies on cookies!

Create CRM Record

Use the information obtained from the email to create a new record in the CRM system. This involves mapping email fields to the corresponding CRM fields associating the communication with the relevant contact or account.

That’s the hard bit over! All that’s left now is dealing with any user errors, testing, deployment and getting approval from Microsoft.

As a reminder, this is the second of a three-part series; you can find parts one and three below.

If you are interested in working with McKenna Consultants on your Outlook add-in and CRM integration project, please get in touch with us today.

Nick McKenna
Since 2004, Nick McKenna, BSc, MBCS Biography has been the CEO of McKenna Consultants. McKenna Consultants is a bespoke software development based in North Yorkshire, specialising in Cloud development, mobile App development, progressive web App development, systems integration and the Internet of Things development. Nick also holds a First Class Degree in Computer Science (BSc) and wrote his first computer program at the age of nine, on a BBC Micro Model B computer. For the last 21 years, Nick has been a professional computer programmer and software architecture. Nick’s technical expertise includes; Net Core, C#, Microsoft Azure, Asp.Net, RESTful web services, eProcurement, Swift, iOS mobile development, Java, Android mobile development, C++, Internet Of Things and more. In addition, Nick is experienced in Agile coaching, training and consultancy, applying modern Agile management techniques to marketing and running McKenna Consultants, as well as the development of software for clients. Nick is a Certified Enterprise Coach (Scrum Alliance), SAFe Program Consultant (SAI), Certified LeSS Practitioner (LeSS) and Certified Scrum@Scale Practitioner. Outside the office, Nick is a professional scuba diver and he holds the rank of Black Belt 5th Dan in Karate.