MailKit OAuth2 SMTP Office365 发送邮件

问题描述 投票:0回答:1

自从 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)

有人有想法吗?

c# oauth-2.0 smtp office365 mailkit
1个回答
0
投票

正如您所发现的,您需要使用客户端凭据工作流程。

我已在此处记录了您需要执行的操作:https://github.com/jstedfast/MailKit/blob/master/ExchangeOAuth2.md(具体请参阅Web Services文档)。

重新发布到这里是为了让 StackOverflow 高兴。

向 Microsoft 注册您的应用程序

无论您是在编写桌面、移动还是 Web 服务应用程序,您需要做的第一件事就是注册您的 与 Microsoft 身份平台的应用程序。为此,请访问 Microsoft 快速入门指南 并按照说明进行操作。

为您的应用程序配置正确的 API 权限

您可能需要配置多种不同的 API 权限,具体取决于您的应用程序打算使用的协议。

按照说明进行操作将 POP、IMAP 和/或 SMTP 权限添加到您的 Entra AD 应用程序

网络服务

为您的 Web 服务注册服务主体

注册您的 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 服务权限

为了授予您的 Web 服务访问 Office365 和/或 Exchange 帐户的权限,您需要首先获取 使用以下命令在上一步中注册的服务主体 ID:

Get-ServicePrincipal | fl

获得 Web 服务的服务主体 ID 后,请使用以下命令添加完整的 您的网络服务将访问的电子邮件帐户的邮箱权限:

Add-MailboxPermission -Identity "[email protected]" -User 
<SERVICE_PRINCIPAL_ID> -AccessRights FullAccess

使用 OAuth2 验证 Web 服务

现在您已经有了 Client IDTenant 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 文档。

© www.soinside.com 2019 - 2024. All rights reserved.