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.
- Part One – Enhancing CRM Integration: Building an MS Outlook Add-In with the Microsoft Graph API
- Part Three – Testing and Approval
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:
- 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:
Place | Scopes To Copy In |
Your Add-in Manifest | openid profile offline_access User.Read Mail.Read Files.ReadWrite |
Your App Registration in Azure | Email openid profile offline_access User.Read Mail.Read Files.ReadWrite |
In Your Add-in Code | User.Read Mail.Read Files.ReadWrite |
In Your Server-Side Code When Performing Code Exchange | User.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:
- Your core CRM system
- 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:
- Tell Graph API to use only immutable IDs
- 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.
- Part One – Enhancing CRM Integration: Building an MS Outlook Add-In with the Microsoft Graph API
- Part Three – Testing and Approval
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.