OAuth 2.0 for Office 365 Accounts (non-interactive mode)

This tutorial demonstrates using OAuth 2.0 with IMAP/POP3/SMTP for Office 365 (Client Credentials flow where the end user is not required to give consent to your app's accessing their email).

See Microsoft accounts - Installed apps or Microsoft accounts - Installed apps running HttpListener versions of this tutorial if you're looking for OAuth 2.0 for Outlook.com or Hotmail.com accounts (not Office 365).

See desktop or ASP.NET Core guide if you're looking for interactive flows where the end user grants consent to your app to access their email.

Introduction

Basically, Client Credentials flow is very simple on the application side but in case of Office 365 requires a lot of efforts to configure a number of things in your Office 365 tenant and app registration. The below is an unofficial guide which seems to work for us. It was adapted from the kind contribution of our customers and our personal experience and, lately, Authenticate an IMAP, POP or SMTP connection using OAuth article. However, should you encounter any issues with it, we recommend contacting Office 365 techsupport directly as we cannot assist there.

Register Azure project

Configure Exchange Online

It's now required to set permissions to impersonate user mailboxes in Exchange Online. For that, you to create a Service Principal and assign it to all mailboxes which you want to access. The mailboxes must belong to your tenant/domain.

Get access token - Sample code

Now you can use Microsoft APIs to get the access token and pass it to MailBee.NET to log in the desired mailbox in your Office 365 tenant.

The code will be the same for desktop, console and web applications.

// Add to the top of your code file (if not already present)
using System;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using MailBee;
using MailBee.Pop3Mail;
using MailBee.ImapMail;
using MailBee.SmtpMail;

...

// Add to an async method
string clientId = "Application (client) ID";
string tenantId = "Directory (tenant) ID";
string clientSecret = "Saved Client Secret";
string userEmail = "mailbox, e.g. mailbox@yourdomain.com";
string[] scopes = { "https://outlook.office365.com/.default" };
IConfidentialClientApplication confidentalApp = ConfidentialClientApplicationBuilder.Create(clientId).WithTenantId(tenantId).WithClientSecret(clientSecret).Build();
AuthenticationResult result = await confidentalApp.AcquireTokenForClient(scopes).ExecuteAsync();
string userName = result.Account?.Username;
string accessToken = result.AccessToken;
string xOAuthKey = OAuth2.GetXOAuthKeyStatic(userEmail, accessToken);

// IMAP login
Imap imap = new Imap();
await imap.ConnectAsync("outlook.office365.com", 993);
await imap.LoginAsync(null, xOAuthKey, AuthenticationMethods.SaslOAuth2, AuthenticationOptions.None, null);
await imap.DisconnectAsync();

// POP3 login
Pop3 pop = new Pop3();
await pop.ConnectAsync("outlook.office365.com", 995);
await pop.LoginAsync(null, xOAuthKey, AuthenticationMethods.SaslOAuth2, AuthenticationOptions.None, null);
await pop.DisconnectAsync();

// SMTP e-mail send
Smtp mailer = new Smtp();
SmtpServer smtpServ = new SmtpServer("smtp.office365.com", 587, 0);
smtpServ.AccountName = null;
smtpServ.Password = xOAuthKey;
smtpServ.AuthMethods = AuthenticationMethods.SaslOAuth2;
mailer.SmtpServers.Add(smtpServ);
mailer.From.Email = userEmail;
mailer.To.Add(userEmail);
mailer.Subject = "Email to myself";
await mailer.SendAsync();
' Add to the top of your code file (if not already present)
Imports Microsoft.Identity.Client
Imports MailBee
Imports MailBee.Pop3Mail
Imports MailBee.ImapMail
Imports MailBee.SmtpMail

...

' Add to an async method
Dim clientId As String = "Application (client) ID"
Dim tenantId As String = "Directory (tenant) ID"
Dim clientSecret As String = "Saved Client Secret"
Dim userEmail As String = "mailbox, e.g. mailbox@yourdomain.com"
Dim scopes As String() = {"https://outlook.office365.com/.default"}
Dim confidentalApp As IConfidentialClientApplication = ConfidentialClientApplicationBuilder.Create(clientId).WithTenantId(tenantId).WithClientSecret(clientSecret).Build()
Dim result As AuthenticationResult = Await confidentalApp.AcquireTokenForClient(scopes).ExecuteAsync()
Dim userName As String = result.Account?.Username
Dim accessToken As String = result.AccessToken
Dim xOAuthKey As String = OAuth2.GetXOAuthKeyStatic(userEmail, accessToken)

' IMAP login
Dim imap As Imap = New Imap()
Await imap.ConnectAsync("outlook.office365.com", 993)
Await imap.LoginAsync(Nothing, xOAuthKey, AuthenticationMethods.SaslOAuth2, AuthenticationOptions.None, Nothing)
Await imap.DisconnectAsync()

' POP3 login
Dim pop As Pop3 = New Pop3()
Await pop.ConnectAsync("outlook.office365.com", 995)
Await pop.LoginAsync(Nothing, xOAuthKey, AuthenticationMethods.SaslOAuth2, AuthenticationOptions.None, Nothing)
Await pop.DisconnectAsync()

' SMTP e-mail send
Dim mailer As Smtp = New Smtp()
Dim smtpServ As SmtpServer = New SmtpServer("smtp.office365.com", 587, 0)
smtpServ.AccountName = Nothing
smtpServ.Password = xOAuthKey
smtpServ.AuthMethods = AuthenticationMethods.SaslOAuth2
mailer.SmtpServers.Add(smtpServ)
mailer.From.Email = userEmail
mailer.To.Add(userEmail)
mailer.Subject = "Email to myself"
Await mailer.SendAsync()

If you're on .NET 4.5 and experiencing TLS 1.0 related error when obtaining the token, upgrade your solution to a newer .NET version. Alternatively, add this at the top of your method which gets the access token:

System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12

The login procedure should now complete successfully.

You can find the complete sample projects based on this code (.NET 4.5+ compatible, C# and VB.NET) at these locations:

As mentioned above, the code in these console projects can be applied to desktop and ASP.NET Core apps with no changes.


Send feedback to AfterLogic

Copyright © 2006-2023 AfterLogic Corporation. All rights reserved.