Поскольку Microsoft объявила об удалении базовой поддержки smtp для аутентификации, я пытаюсь найти другой способ отправки электронных писем из моего приложения. Мои приложения являются серверными приложениями, поэтому это должен быть неинтерактивный поток.
Когда я использую поток паролей, все работает отлично, однако поток предоставления пароля является устаревшим, поскольку он также раскрывает пароль пользователя:
static async Task<string> GetAccessToken(FormUrlEncodedContent content, string tokenEndpoint)
{
var client = new HttpClient();
var response = await client.PostAsync(tokenEndpoint, content).ConfigureAwait(continueOnCapturedContext: false);
var jsonString = await response.Content.ReadAsStringAsync();
client.Dispose();
var doc = JsonDocument.Parse(jsonString);
JsonElement root = doc.RootElement;
if (root.TryGetProperty("access_token", out JsonElement tokenElement))
return tokenElement.GetString()!;
throw new Exception("Failed to get access token");
}
static void SendO365(SaslMechanism accessToken, string host, int port, string from, string to)
{
using (var client = new SmtpClient())
{
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
try
{
client.Connect(host, port, SecureSocketOptions.Auto);
client.Authenticate(accessToken);
var msg = new MimeMessage();
msg.From.Add(MailboxAddress.Parse(from));
msg.To.Add(MailboxAddress.Parse(to));
msg.Subject = "Testing SMTP";
msg.Body = new TextPart("plain") { Text = "This is a test message." };
client.Send(msg);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
var content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"),
new KeyValuePair<string, string>("client_secret", "yyy"),
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("resource", "https://outlook.office365.com"),
new KeyValuePair<string, string>("scope", ".default"),
new KeyValuePair<string, string>("username", "[email protected]"),
new KeyValuePair<string, string>("password", "zzz"),
});
string tenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"
string tokenEndpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/token";
var accessToken = GetAccessToken(content, tokenEndpoint).Result;
var userEmail = "[email protected]";
var smtpServer = "smtp.office365.com";
var smtpPort = 587;
var toEmail = "[email protected]";
SendO365(new SaslMechanismOAuth2(userEmail, accessToken), smtpServer, smtpPort, userEmail, toEmail);
В Entra я установил область действия Mail.Send.
Я попробовал переключиться на поток client_credentials, так как он более безопасен, но всегда получаю сообщение об ошибке Authentication unsuccessful на client.Authenticate(accessToken).
У кого-нибудь есть идея?
Редактировать
Репозиторий GitHub: https://github.com/tsafadi/MailKit-OAuth-SMTP/tree/master/MailKit.SMTP-OAuth





Как вы обнаружили, вам необходимо использовать рабочий процесс с учетными данными клиента.
Я задокументировал, что вам нужно сделать, здесь: https://github.com/jstedfast/MailKit/blob/master/ExchangeOAuth2.md (в частности, см. документацию по веб-службам).
Повторная публикация здесь, чтобы порадовать StackOverflow.
Независимо от того, пишете ли вы настольное, мобильное или веб-приложение, первое, что вам нужно сделать, это зарегистрировать свое приложение с платформой идентификации Microsoft. Для этого зайдите на сайт Microsoft Краткое руководство и следуйте инструкциям.
Существует несколько различных разрешений API, которые вы можете настроить в зависимости от того, какие протоколы намерено использовать ваше приложение.
Следуйте инструкциям по добавлению разрешений POP, IMAP и/или SMTP в приложение Entra AD.
После регистрации вашей веб-службы администратору клиента необходимо будет зарегистрировать вашего субъекта службы.
Чтобы использовать командлет New-ServicePrincipal, откройте Azure Powershell. терминал, установите ExchangeOnlineManagement и подключитесь к своему арендатору, как показано ниже:
Install-Module -Name ExchangeOnlineManagement -allowprerelease
Import-module ExchangeOnlineManagement
Connect-ExchangeOnline -Organization <tenantId>
Затем зарегистрируйте принципала службы для вашего веб-сервиса:
New-ServicePrincipal -AppId <APPLICATION_ID> -ObjectId <OBJECT_ID> [-Organization <ORGANIZATION_ID>]
Чтобы предоставить вашей веб-службе разрешения на доступ к учетной записи Office365 и/или Exchange, вам необходимо сначала получить Идентификатор участника службы, зарегистрированный на предыдущем шаге с помощью следующей команды:
Get-ServicePrincipal | fl
Получив идентификатор участника службы для вашей веб-службы, используйте следующую команду, чтобы добавить полный Разрешения почтового ящика для учетной записи электронной почты, к которой будет обращаться ваша веб-служба:
Add-MailboxPermission -Identity "[email protected]" -User
<SERVICE_PRINCIPAL_ID> -AccessRights FullAccess
Теперь, когда у вас есть строки Client ID и Tenant ID, вам нужно будет подключить эти значения в ваше приложение.
В следующем примере кода используется Microsoft.Identity.Client. пакет nuget для получения токена доступа, который понадобится MailKit для передачи на Exchange. сервер.
var confidentialClientApplication = ConfidentialClientApplicationBuilder.Create (clientId)
.WithAuthority ($"https://login.microsoftonline.com/{tenantId}/v2.0")
.WithCertificate (certificate) // or .WithClientSecret (clientSecret)
.Build ();
var scopes = new string[] {
// For IMAP and POP3, use the following scope
//"https://ps.outlook.com/.default"
// For SMTP, use the following scope
"https://outlook.office365.com/.default"
};
var authToken = await confidentialClientApplication.AcquireTokenForClient (scopes).ExecuteAsync ();
var oauth2 = new SaslMechanismOAuth2 (accountEmailAddress, authToken.AccessToken);
using (var client = new SmtpClient ()) {
client.Connect ("smtp.office365.com", 587, SecureSocketOptions.SslOnConnect);
client.Authenticate (oauth2);
client.Send (message);
client.Disconnect (true);
}
Для получения дополнительной информации посетите Microsoft.Identity.Client документация.
Я попробую это, спасибо!
Я выполнил все в точности, как вы сказали, но продолжаю получать 535: 5.7.3 Authentication unsuccessful [FR4P281CA0165.DEUP281.PROD.OUTLOOK.COM 2024-06-13T14:41:10.853Z 08DC8A68B4EFA073]
Нет, пробовал несколько раз и даже все удалял, но не работает. Есть несколько вещей, которые меня смущают. Во-первых, в предоставленной вами статье MSFT, в которой показано, как добавлять разрешения к приложению, показан снимок экрана с «SMTP.SendAsApp», который я не могу найти. Я предполагаю, что теперь это Mail.Send. Кроме того, когда я пытаюсь создать своего субъекта-службы, я не могу использовать идентификатор объекта регистрации приложения, мне нужно использовать идентификатор объекта корпоративного приложения. Это все очень запутанно, и я не могу заставить это работать
Я создал репозиторий GitHub. Может быть, вы сможете проверить это и посмотреть, есть ли что-то не так.
Нет, разрешение Mail.Send предназначено ТОЛЬКО для HTTP REST API (также известного как Microsoft.Graph API) и НЕ РАЗРЕШАЕТ отправку SMTP. Вот почему вы терпите неудачи.
Это работает, большое спасибо! Я действительно использовал неправильное разрешение.
Мне нужно использовать SMTP и IMAP с OAUTH2 на адресе электронной почты потребителя Outlook.com (т. е. без активного каталога). Во всех решениях говорится о наличии клиента Azure с активной подпиской. У меня нет такого. Как использовать OAUTH2 на потребительском адресе электронной почты Outlook.com с помощью MailKit?
Вам нужна учетная запись Azure с клиентом, потому что именно так вы регистрируете свою программу. Без этого вы не сможете получить идентификатор клиента или секрет, необходимые для процесса аутентификации OAuth2. Outlook, Thunderbird и т. д. имеют свои собственные идентификаторы/секреты клиентов, которые компании, владеющие этими приложениями, зарегистрировали в Azure. Но если вы пишете собственную программу, вам понадобится собственная учетная запись Azure. Обойти это невозможно.