OAuth 2.0 device flow with Office365/Exchange IMAP/POP3/SMTP

This article shows how to implement OAuth 2.0 device flow to access Office365 via IMAP, POP3 or SMTP using Mail.dll .net email client.

Device flow allows operator/administrator to authenticate your application on a different machine than your application is installed.

Make sure IMAP/POP3/SMTP 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:

Then you need to apply correct API permissions and grant the admin consent for your domain.

In the API permissions / Add a permission wizard, select Microsoft Graph and then Delegated permissions to find the following permission scopes listed:

  • offline_access
  • email
  • IMAP.AccessAsUser.All
  • POP.AccessAsUser.All
  • SMTP.Send

Remember to Grant admin consent:

Use Microsoft Authentication Library for .NET (MSAL.NET) nuget package to obtain an access token:

string clientId = "Application (client) ID";
string tenantId = "Directory (tenant) ID";

IPublicClientApplication app = PublicClientApplicationBuilder
// This allows saving access/refresh tokens to some storage

var scopes = new string[] 

Now acquire an access token and a user name:

string userName;
string accessToken;

var account = (await app.GetAccountsAsync()).FirstOrDefault();
    AuthenticationResult refresh = await app
        .AcquireTokenSilent(scopes, account)

    userName = refresh.Account.Username;
    accessToken = refresh.AccessToken;
catch (MsalUiRequiredException e)
    var acquire = await app.AcquireTokenWithDeviceCode(
        // Write url and code to logs so the operator can react:

        // This happens on the first run, manually,
        //  on the operator machine.
        // The code below code is only to illustrate 
        // the operator opening browser on his machine,
        // opening the url and using the code 
        // (extracted from the application logs)
        // to authenticate the app.
            new ProcessStartInfo(result.VerificationUrl) 
                        { UseShellExecute = true }

        return Task.CompletedTask;

    userName = acquire.Account.Username;
    accessToken = acquire.AccessToken;

AcquireTokenWithDeviceCode call waits until operator/administrator gives consent by going to VerificationUrl, entering UserCode and authenticating – this usually happens on a different machine than the application is installed.

Finally your app will exit AcquireTokenWithDeviceCode method and connect using IMAP/POP3/SMTP, authenticate and download emails:

using (Imap client = new Imap())
    client.LoginOAUTH2(userName, accessToken);

    // ...


You can find more details on this flow here:


Token serialization

Below is a simple implementation that saves MSAL token cache to file. Please note that most likely you should store this cache in an encrypted form:

static class TokenCacheHelper
    public static void EnableSerialization(ITokenCache tokenCache)

    private static readonly string _fileName = "msalcache.bin3";

    private static readonly object _fileLock = new object();

    private static void BeforeAccessNotification(TokenCacheNotificationArgs args)
        lock (_fileLock)
            byte[] data = null;
            if (File.Exists(_fileName))
                data = File.ReadAllBytes(_fileName);

    private static void AfterAccessNotification(TokenCacheNotificationArgs args)
        if (args.HasStateChanged)
            lock (_fileLock)
                byte[] data = args.TokenCache.SerializeMsalV3();
                File.WriteAllBytes(_fileName, data);

More details on MSAL token serialization are available here:


Extending Sign-in frequency with policies

You can extend how often operator needs to re-authenticate the application up to 1 year:

Side note: Have in mind that similarly a client credential flow requires a client secret which is valid for 2 years maximum.

Get Mail.dll



Consider using our Q&A forum for asking questions.