OAuth 2.0 Support for IMAP and POP3

In the recent months, Exchange online deprecated basic authentication with the IMAP and POP3 protocols requiring the switch to OAuth 2.0 authentication for the two protocols. Here’s a sample application demonstrating methods of authenticating with OAuth and IMAP/POP3 with an interactive login.

The code utilizes a library called MailKit which manages creating the authentication wrapper.

Details on the deprecation and information on how to authenticate can be found in the Microsoft document below.
https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth

The necessary permission need to be granted to the app registration in AAD can be seen below. You will only need to set the respective permission for the protocol the application is using.

using System;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
using System.Collections.Generic;
using MailKit.Net.Imap;
using MailKit.Net.Pop3;
using MailKit.Security;
using MailKit;
using MimeKit.Cryptography;
using System.Security;
using Microsoft.Identity.Client;

string clientId = "<ClientId>";
string tenantId = "<TenantId>";
string authorizationEndpoint = $"https://login.microsoftonline.us/{tenantId}/oauth2/v2.0/authorize";
string tokenEndpoint = $"https://login.microsoftonline.us/{tenantId}/oauth2/v2.0/token";
string Endpoint = "outlook.office365.us";
int imapPort = 993;
int pop3Port = 995;
string username = "User UPN";

try{
var result = await AuthenticateMailbox(clientId, tenantId) ;
string accessToken = result.AccessToken;

Console.WriteLine("Testing IMAP........");
//Testing IMAP
using (var client = new MailKit.Net.Imap.ImapClient())
{
    client.Connect(Endpoint, imapPort, SecureSocketOptions.SslOnConnect);
    client.Authenticate(new SaslMechanismOAuth2(username, accessToken));
    var inbox = client.Inbox;
    inbox.Open(FolderAccess.ReadOnly);
    Console.WriteLine($"Total messages: {inbox.Count}");
    client.Disconnect(true);
}
//Testing POP
Console.WriteLine("Testing POP...........");
using (var client = new MailKit.Net.Pop3.Pop3Client())
{
    client.Connect(Endpoint, pop3Port, SecureSocketOptions.SslOnConnect);
    client.Authenticate(new SaslMechanismOAuth2(username, accessToken));
    for (int i = 0; i < client.Count; i++)
    {
        var message = client.GetMessage(i);
        Console.WriteLine("Subject: {0}", message.Subject);
    }
    client.Disconnect(true);
}
}
catch(Exception ex)
{
    throw ex;
}

async Task<Microsoft.Identity.Client.AuthenticationResult> AuthenticateMailbox(string appId, string directoryId)
{
    try
    {
        IPublicClientApplication app = PublicClientApplicationBuilder.Create(appId)
        .WithAuthority(AzureCloudInstance.AzureUsGovernment, directoryId)
        .WithRedirectUri("http://localhost")
        .Build();

        //scope for Commercial - https://outlook.office.com/IMAP.AccessAsUser.All and https://outlook.office.com/POP.AccessAsUser.All
        //scope for Azure Government (GCC-High) - https://outlook.office365.us/IMAP.AccessAsUser.All and https://outlook.office365.us/POP.AccessAsUser.All
        var scopes = new string[]
        {
            "email",
            "https://outlook.office365.us/IMAP.AccessAsUser.All",
            "https://outlook.office365.us/POP.AccessAsUser.All"
        };
        Microsoft.Identity.Client.AuthenticationResult result;

        result = await app.AcquireTokenInteractive(scopes).WithUseEmbeddedWebView(false).ExecuteAsync();
        return result;
    }
    catch (Exception ex)
    {
        throw ex;
    }
}