我们一直尝试使用 Office365 SMTP OAUTH2 身份验证和客户端凭据流程,但没有成功。
文档声称 SMTP 应该可以工作
还声明以下内容:
<
我们可以使用代码交互流程和委托动态范围生成令牌 https://outlook.office.com/SMTP.Send
生成的令牌具有“SMTP.Send”范围,可在 JavaMail 中使用它来成功发送来自特定用户的电子邮件。
我们正在构建一个非交互式应用程序,上述内容对我们不起作用。
当我们尝试使用客户端凭证流生成令牌时,唯一支持的范围格式是 {resource}/.default
HTTP POST https://login.microsoftonline.com/{tenantid}/oauth2/v2.0/token
client_id=...
client_secret=...
grant_type=client_credentials
scope=https://outlook.office365.com/.default
我们无法在 Microsoft Office API 下设置应用程序的 SMTP 权限。
认证总是返回“535 5.7.3 认证失败”
这应该像 IMAP 一样工作。
我们发现的唯一选项是在
下禁用安全默认值Azure 活动目录 -> 属性 -> 管理安全默认值
这将启用纯文本身份验证。
您还需要确保您的邮箱没有使用以下 powershell 命令禁用 Smtp 客户端身份验证
设置-CASMailbox -Identity -SmtpClientAuthenticationDisabled $false
经过这两项更改后,JavaMail 可以使用用户/密码进行身份验证并可以发送电子邮件。
现在支持非交互式登录的 SMTP Oauth 2.0 客户端凭证流程
注意 根据当前的 SMTP Oauth 2.0 测试,不支持非交互式登录的客户端凭据流。
我自己才发现这一点。
Microsoft Graph API 是一种替代选项,我已确认它确实可以与客户端凭据流程配合使用,但它还有与需要考虑的卷和文件附件相关的其他限制。
在努力解决这个问题并最终找到了使这个工作正常运行所需的所有部分之后,我想我会分享所有步骤是什么。我需要我的服务器团队执行一些步骤,因为我无权访问其中一些步骤,这些都已注明。
使用 OAuth 身份验证而不是基本身份验证发送电子邮件:
然后在代码中流程是这样的:
private async Task SendAsync(MimeMessage mailMessage)
{
using (var client = new SmtpClient())
{
try
{
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
await client.ConnectAsync(_emailConfig.SmtpServer, _emailConfig.MailPort, MailKit.Security.SecureSocketOptions.StartTls);
//Authenticate using office365 access token
var accessToken = await GetAccessToken();
await client.AuthenticateAsync(new SaslMechanismOAuth2(_emailConfig.SmtpUser, accessToken));
await client.SendAsync(mailMessage);
}
catch (Exception ex)
{
//fallback to internal server incase errors with office 365
await client.DisconnectAsync(true);
await client.ConnectAsync(_emailConfig.SmtpServerError);
await client.SendAsync(mailMessage);
}
finally
{
await client.DisconnectAsync(true);
client.Dispose();
}
}
}
//Get an office365 access token for the app
public async Task<string> GetAccessToken()
{
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, https://login.microsoftonline.com/ + _emailConfig.TenantId + "/oauth2/v2.0/token");
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = _emailConfig.ClientId,
["client_secret"] = _emailConfig.ClientSecret,
["scope"] = https://outlook.office365.com/.default
});
var response = await client.SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
var responseJson = JObject.Parse(responseString);
return responseJson.Value<string>("access_token");
}
}
}