OAuth 2.0 client credential flow with Office365/Exchange IMAP/POP3

This article shows how to implement OAuth 2.0 client credential flow to access Office365 via IMAP, POP3 using Mail.dll .net email client. This flow is particularly useful for daemon/service apps that need to monitor certain mailboxes, without any user interaction.

Make sure IMAP/POP3 is enabled for your organization and mailbox:
Enable IMAP/POP3/SMTP in Office 365

Register your application in Azure Portal, here’s a detailed guide how to do that:
https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app

Add permissions to your application in the API permissions / Add a permission wizard:

Select APIs my organization uses and search for Office 365 Exchange Online:

…then click Application permissions:

For POP access, choose the POP.AccessAsApp permission.
For IMAP access, choose the IMAP.AccessAsApp permission.

Remember to Grant admin consent:

Create an application secret in Certificates & secrets panel by clicking ‘New client secret’ button:

Note the secret value as it is shown only during creation.

Use Windows PowerShell on your machine to Register service principals in Exchange.

Set execution policy first:

Set-ExecutionPolicy RemoteSigned

Install ExchangeOnlineManagement module:

Install-Module -Name ExchangeOnlineManagement 
Import-Module ExchangeOnlineManagement 

Connect and log-in as an administrator (you’ll be prompted for password):

Connect-ExchangeOnline
 -UserPrincipalName your-admin-account@your-domain.onmicrosoft.com

For Exchange running in hybrid mode log-in using following code:

$lc = Get-Credential
Connect-ExchangeOnline -Credential $lc

Create service principal

New-ServicePrincipal
 -AppId <APPLICATION_ID>
 -ServiceId <OBJECT_ID> 
 [-Organization <ORGANIZATION_ID>]

You can find ApplicationId and ObjectId in Enterprise applications in your application’s Overview panel:

Make sure you use the Object ID from the Enterprise Application

Do not use the value from the App Registration screen. 

In our case:

New-ServicePrincipal
 -AppId 061851f7-08c0-40bf-99c1-ebd489c11f16
 -ServiceId 4352fc11-5c2f-4b0b-af40-447ff10664e8

Note: If you still get an error running the New-ServicePrincipal cmdlet after you perform these steps, it is likely due to the fact that the user doesn’t have enough permissions in Exchange online to perform the operation. By default this cmdlet is available to users assigned the Role Management role

Add permissions to a specific mailbox:

Add-MailboxPermission
 -Identity "<USER@your-domain.onmicrosoft.com>"
 -User <OBJECT_ID>
 -AccessRights FullAccess

In our case:

Add-MailboxPermission
 -Identity "AdeleV@your-domain.onmicrosoft.com"
 -User 4352fc11-5c2f-4b0b-af40-447ff10664e8
 -AccessRights FullAccess

Shared mailboxes

You need to use Add-MailboxPermission for every shared mailbox you need access to:

Add-MailboxPermission
 -Identity "shared@your-domain.onmicrosoft.com"
 -User <OBJECT_ID>
 -AccessRights FullAccess

Let’s code

Use Microsoft Authentication Library for .NET (MSAL.NET) nuget package to obtain an access token:
https://www.nuget.org/packages/Microsoft.Identity.Client/

string clientId = "Application (client) ID";    // 061851f7-...
string tenantId = "Directory (tenant) ID";
string clientSecret = "Client secret value";

string userEmail = "Username for mailbox";    // AdeleV@...

var app = ConfidentialClientApplicationBuilder
    .Create(clientId)
    .WithTenantId(tenantId)
    .WithClientSecret(clientSecret)
    .Build();

string[] scopes = new string[] { 
        "https://outlook.office365.com/.default" 
    };

Now acquire an access token:

var result = await app.AcquireTokenForClient(scopes)
    .ExecuteAsync();

string accessToken = result.AccessToken;

Finally you can connect using IMAP/POP3, authenticate and download user’s emails:

using (Imap client = new Imap())
{
    client.ConnectSSL("outlook.office365.com");
    client.LoginOAUTH2(userName, accessToken);
 
    client.SelectInbox();

    List<long> uids = imap.Search(Flag.Unseen);
    foreach (long uid in uids)
    {
        IMail email = new MailBuilder()
                .CreateFromEml(imap.GetMessageByUID(uid));
        string subject = email.Subject;
   }

    client.Close();
} 

SMTP

Currently Microsoft doesn’t support client credential flow + SMTP.

For this flow only POP3 and IMAP permissions are available: POP.AccessAsApp for POP3 client access and IMAP.AccessAsApp for IMAP client access

All other OAuth flows (webdesktoppassword grantdevice) support SMTP client access.

SMTP AUTH will still be available when Basic authentication is permanently disabled on October 1, 2022.” (https://docs.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/deprecation-of-basic-authentication-exchange-online)

However Microsoft disables SMTP AUTH in all tenants in which it’s not being used.

Here’s how to enable SMTP AUTH:
https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/authenticated-client-smtp-submission

Troubleshooting

1. Start with PowerShell commands:

Get-ServicePrincipal
Get-MailboxPermission -Identity "AdeleV@your-domain.onmicrosoft.com"

You should see following results:

Make sure the ServiceId is the same as the Object ID on the Enterprise Application screen (do not use the value from the App Registration screen)

Make sure the AppId is the same as the Application ID on the Enterprise Application screen

2. Check if you can connect to this account using IMAP and regular interactive flow:

https://www.limilabs.com/blog/office-365-oauth-2-0-imap-pop3-email-client-connectivity-tools

This proves you have IMAP access properly configured.

3. Check if you added correct permissions and have granted Admin consent for your domain.

4. Usually people use incorrect client/tenant ids/secrets – double check every single value you enter (also for additional spaces).

5. You may need to wait 20-30 minutes for some changes to take effect (it really may take this long!).

Additional links

https://docs.microsoft.com/en-us/powershell/exchange/exchange-online-powershell-v2?view=exchange-ps#install-and-maintain-the-exo-v2-module
https://docs.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#use-client-credentials-grant-flow-to-authenticate-imap-and-pop-connections

Tags:     

Questions?

Consider using our Q&A forum for asking questions.