I have followed this guide on how to use client credentials grant flow to authenticate IMAP.
The app registration has the following permissions:
I have successfully given my application's service principal access to the mailbox I wish to read:
Add-MailboxPermission -Identity "john.smith@contoso.com" -User <SERVICE_PRINCIPAL_ID> -AccessRights FullAccess
And the application's service principal has SendAs
rights to the mailbox used.
Add-RecipientPermission -Identity "john.smith@contoso.com" -Trustee <SERVICE_PRINCIPAL_ID> -AccessRights SendAs
https://serverfault.com/a/1110785
The obtained access token looks good with roles matching what I want to do:
I have used both Microsoft.Identity.Client
and System.Net.Http.HttpClient
to get a token successfully and I have used MailKit.Net.Smtp.SmtpClient
to send emails. I can get access to the mailbox via IMAP using MailKit.Net.Imap.ImapClient
. See this question:
IMAP and SMTP settings from:
The guide does say:
Note As per the current test with SMTP Oauth 2.0 client credential flow with non-interactive sign in is not supported.
What I can't understand is why they then have a Office 365 Exchange Online
Application permission
called Mail.Send
that Allows the app to send mail as any user without a signed-in user
. Application permissions states that they should be used when Your application runs as a background service or daemon without a signed-in user.
Similar question:
Office 365 XOAUTH2 for SMTP on native clients 535 5.7.3 Authentication unsuccessful
Using Mail.Send
with Delegated permissions
I can send an email using SMTP with the mailbox I wish to send as. Code example for that:
https://stackoverflow.com/a/73728638/3850405
Code:
var clientId = "";
var tenantId = "";
var clientSecret = "";
//I have also tried with var outlookDomain = "smtp.office365.com";
var outlookDomain = "outlook.office365.com";
var email = "info@example.com";
var app = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(clientSecret)
.Build();
string[] scopes = new string[] {
"https://outlook.office365.com/.default"
};
var result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
string accessToken = result.AccessToken;
var oauth2Test = new SaslMechanismOAuth2(email, accessToken);
var client = new ImapClient();
await client.ConnectAsync(outlookDomain, 993, SecureSocketOptions.Auto, cancellationToken);
await client.AuthenticateAsync(oauth2Test);
await client.DisconnectAsync(true);
var message = new MimeMessage();
message.From.Add(MailboxAddress.Parse(_settings.UserName));
message.To.Add(MailboxAddress.Parse("info2@example.com"));
message.Subject = "Test";
message.Body = new TextPart("plain") { Text = @"Oscar Testar" };
using (var smtpClient = new SmtpClient())
{
await smtpClient.ConnectAsync("smtp.office365.com", 587, SecureSocketOptions.StartTls, cancellationToken);
if (smtpClient.AuthenticationMechanisms.Contains("OAUTHBEARER") || smtpClient.AuthenticationMechanisms.Contains("XOAUTH2"))
{
//Exception here:
await smtpClient.AuthenticateAsync(oauth2Test);
}
await smtpClient.SendAsync(message, cancellationToken);
await smtpClient.DisconnectAsync(true, cancellationToken);
}
MailKit.Security.AuthenticationException: '535: 5.7.3 Authentication unsuccessful [AB1C230CA0059.SWEP280.PROD.OUTLOOK.COM]'
If I try with outlookDomain set to smtp.office365.com
and scopes set to https://smtp.office365.com/.default
I get the following exception:
Microsoft.Identity.Client.MsalServiceException: 'AADSTS500011: The resource principal named https://smtp.office365.com was not found in the tenant named <NAME>. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.