带有 OAUTH2 客户端凭据流的 Office365 SMTP 返回“身份验证失败”

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

我们一直尝试使用 Office365 SMTP OAUTH2 身份验证和客户端凭据流程,但没有成功。

文档声称 SMTP 应该可以工作

https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth

还声明以下内容:

<>

我们可以使用代码交互流程和委托动态范围生成令牌 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 可以使用用户/密码进行身份验证并可以发送电子邮件。

email oauth-2.0 azure-active-directory smtp office365
3个回答

0
投票

根据https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth

注意 根据当前的 SMTP Oauth 2.0 测试,不支持非交互式登录的客户端凭据流。

我自己才发现这一点。

Microsoft Graph API 是一种替代选项,我已确认它确实可以与客户端凭据流程配合使用,但它还有与需要考虑的卷和文件附件相关的其他限制。


0
投票

在努力解决这个问题并最终找到了使这个工作正常运行所需的所有部分之后,我想我会分享所有步骤是什么。我需要我的服务器团队执行一些步骤,因为我无权访问其中一些步骤,这些都已注明。

使用 OAuth 身份验证而不是基本身份验证发送电子邮件:

  1. 在 Entra 中的应用程序注册下注册应用程序。应用程序注册 - Microsoft Entra 管理中心
  2. 从 Entra 应用页面获取租户 ID、客户端 ID,并生成客户端密钥 ID。
  3. 在 API 权限下添加“SMTP.SendAsApp”并授予管理员同意。
  4. 服务器团队:注册服务主体“New-ServicePrincipal -AppId(企业应用ID)-ObjectId xxx”
  5. 服务器团队:添加邮箱权限“Add-MailboxPermission -Identity [电子邮件受保护] -用户“(应用程序名称)”-AccessRights FullAccess”
  6. 如果您想从另一个邮箱发送电子邮件,例如 [电子邮件受保护],请让服务器团队添加“代理发送”权限。

然后在代码中流程是这样的:

    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");
    }
}

}

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