I already found a solution.
It seems that there is some configuration that blocks legacy authentication on Azure AD (try to find Block Legacy Authentication conditional access policy on google). This configuration probably blocks all access over SMTP.
However, during my research, I later switched to the usage of Microsoft API and it works like a charm. I can recommend it. Below is more detailed information on how to use it. The reason on my side is that the whole IT world is slowly (or quickly?) moving towards more secure technologies and spending energy to let SMTP with 2FA work seems to me as a thrown away time. So I stopped myself in the middle of the process, enabled 2FA on the tenant, and implemented a connection to API. The future-proof solution in my eyes. At least for a few years again. Hopefully.
I used specifically Microsoft Graph REST API (docs here).
On Azure, you need to go to your Active Directory, register a new app, setup app permissions and create a client secret. For the API you will need tenant ID, app ID, and your generated client secret what you created.
In your C# project, you need to install NuGet packages Azure.Identity and Microsoft.Graph.
Following code work at on .NET Core 3.1 and .NET 5.0 but I suppose it will work like a charm also for .NET 6.0 (not tested).
Here is part of the code on how to use the API (simplified):
public async Task SendEmail(string senderEmail, string recipientEmail, string messageSubject, string messageBody)
{
// The client credentials flow requires that you request the
// /.default scope, and preconfigure your permissions on the
// app registration in Azure. An administrator must grant consent
// to those permissions beforehand.
var scopes = new[] { "https://graph.microsoft.com/.default" };
// using Azure.Identity;
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "your tenant ID from Azure will be right here";
// At Azure->Azure Active Directory->App Registrations->(your app) you can find all the information about your registered app.
// ID used here is "Application (client) ID" what you can see at this app page in Azure.
//
// Notice also that used App needs to have setup proper privileges in (Azure->Azure Active Directory->App Registration->(your app)->API permissions)
// Necessary privileges for this method and email sending are following privileges:
// Permission type Permissions (from least to most privileged)
// Delegated(work or school account) [Mail.Send]
// Delegated(personal Microsoft account) [Mail.Send]
// Application [Mail.Send]
var azureActiveDirectoryAppClientId = "your app client ID from Azure will be right here";
// At (Azure->Azure Active Directory->App Registrations->(your app)->Certificates & secrets) you can manage credentials here.
// You can find ClientSecret here at mentioned path.
// Be aware that this credentials can not be valid for more than 24 months.
var azureActiveDirectoryAppClientSecret = "your client secret from Azure will be right here";
// https://docs.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
var clientSecretCredential = new ClientSecretCredential(tenantId,
azureActiveDirectoryAppClientId, azureActiveDirectoryAppClientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var message = new Message
{
Subject = messageSubject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = messageBody
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = recipientEmail
}
}
}
};
await graphClient.Users[senderEmail].SendMail(message, null).Request().PostAsync();
}
Also, in the code graphClient.Users[*] you can use directly user ID from Azure (in the place of '*' char).
Hopefully, it will help. I focused more on the code snippet because it can say a lot and you can reuse it quite quickly.
The whole solution seems to be trivial in the end. The tricky part was in my eyes to find all the information.