I have to build a .Net console application that runs on server side as a batch, and sends email to recipients using Office365 and MailKit.
Our company has recently introduced MultiFactor Authentication; I have already rewrite an interactive app that let user to send email using MailKit and MFA, but I am not able to implement the same functionality in an unattended app.
I already register the application on Microsoft Azure, create the Client Secret and implement the following class:
public class Mailer {
public async static void Send(string subject, string body) {
string tenantID = "abcdef";
string clientId = "ghilmn";
string clientSecret = "opqrst";
string appName = "MyApp";
//clientSecret = Uri.EscapeDataString(clientSecret);
//clientSecret = Uri.EscapeUriString(clientSecret);
var MyConfidentialClientApplication = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithTenantId(tenantID)
.Build();
var scopes = new string[] {"https://graph.microsoft.com/.default"};
try {
var authToken = await MyConfidentialClientApplication.AcquireTokenForClient(scopes).ExecuteAsync();
var oauth2 = new SaslMechanismOAuth2(appName, authToken.AccessToken); // SaslMechanismOAuth2(appName, authToken.AccessToken);
var m_client = new SmtpClient(new ProtocolLogger("smtp.log"));
await m_client.ConnectAsync("outlook.office365.com", 587, SecureSocketOptions.StartTls);
await m_client.AuthenticateAsync(oauth2);
}
catch (Exception ex) {
Debug.WriteLine(ex.Message);
// do nothing;
}
return;
}
While the token seems to be correctly acquired (the authToken.AccessToken is not null), the
await m_client.AuthenticateAsync(oauth2);
generate the following error:
Eccezione generata: 'MailKit.Security.AuthenticationException' in System.Private.CoreLib.dll
535: 5.7.3 Authentication unsuccessful [ZR0P278CA0036.CHEP278.PROD.OUTLOOK.COM]
Basically I don't understand
- if the SaslMechanismOAuth2 is the correct one to be invoked
- if yes, what userName shall I pass to the constructor of oauth2; i tried with AppName, AppID and even with user email, but with no success.
Any hint will be appreciated, thanks in advance.
Paolo
P.S. the interactive app that is working uses the PublicClientApplicationBuilder and the AcquireTokenInteractive calls, using the following code.
public async Task Connect_MFA() {
var options = new PublicClientApplicationOptions {
ClientId = "abcd",
TenantId = "efgh",
RedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient"
};
var publicClientApplication = PublicClientApplicationBuilder
.CreateWithApplicationOptions(options)
.Build();
var scopes = new string[] {
"email",
"offline_access",
//"https://outlook.office.com/IMAP.AccessAsUser.All", // Only needed for IMAP
//"https://outlook.office.com/POP.AccessAsUser.All", // Only needed for POP
"https://outlook.office.com/SMTP.Send", // Only needed for SMTP
};
var authToken = await publicClientApplication.AcquireTokenInteractive(scopes).ExecuteAsync();
var oauth2 = new SaslMechanismOAuth2(authToken.Account.Username, authToken.AccessToken);
m_client = new SmtpClient(new ProtocolLogger("smtp.log"));
await m_client.ConnectAsync("outlook.office365.com", 587, SecureSocketOptions.StartTls);
await m_client.AuthenticateAsync(oauth2);
}