自从 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)
。
有人有想法吗?
正如您所发现的,您需要使用客户端凭据工作流程。
我已在此处记录了您需要执行的操作:https://github.com/jstedfast/MailKit/blob/master/ExchangeOAuth2.md(具体请参阅Web Services文档)。
重新发布到这里是为了让 StackOverflow 高兴。
无论您是在编写桌面、移动还是 Web 服务应用程序,您需要做的第一件事就是注册您的 与 Microsoft 身份平台的应用程序。为此,请访问 Microsoft 快速入门指南 并按照说明进行操作。
您可能需要配置多种不同的 API 权限,具体取决于您的应用程序打算使用的协议。
按照说明进行操作将 POP、IMAP 和/或 SMTP 权限添加到您的 Entra AD 应用程序。
注册您的 Web 服务后,租户管理员将需要注册您的服务主体。
要使用 New-ServicePrincipal cmdlet,请打开 Azure Powershell 终端并安装 ExchangeOnlineManagement 并连接到您的租户,如下所示:
Install-Module -Name ExchangeOnlineManagement -allowprerelease
Import-module ExchangeOnlineManagement
Connect-ExchangeOnline -Organization <tenantId>
接下来,为您的 Web 服务注册服务主体:
New-ServicePrincipal -AppId <APPLICATION_ID> -ObjectId <OBJECT_ID> [-Organization <ORGANIZATION_ID>]
为了授予您的 Web 服务访问 Office365 和/或 Exchange 帐户的权限,您需要首先获取 使用以下命令在上一步中注册的服务主体 ID:
Get-ServicePrincipal | fl
获得 Web 服务的服务主体 ID 后,请使用以下命令添加完整的 您的网络服务将访问的电子邮件帐户的邮箱权限:
Add-MailboxPermission -Identity "[email protected]" -User
<SERVICE_PRINCIPAL_ID> -AccessRights FullAccess
现在您已经有了 Client ID 和 Tenant ID 字符串,您需要将这些值插入到 您的申请。
以下示例代码使用 Microsoft.Identity.Client 用于获取 MailKit 传递到 Exchange 所需的访问令牌的 nuget 包 服务器。
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 ImapClient ()) {
await client.ConnectAsync ("outlook.office365.com", 993, SecureSocketOptions.SslOnConnect);
await client.AuthenticateAsync (oauth2);
await client.DisconnectAsync (true);
}
有关更多信息,请查看 Microsoft.Identity.Client 文档。