Graph send.mail首先起作用,然后给出CORS错误

发布于 2025-02-01 07:59:22 字数 4946 浏览 2 评论 0 原文

我有一个.NET 6.0 MVC应用程序,该应用程序使用Microsoft Graph作为登录用户发送电子邮件。我正在使用Microsoft Identity将用户登录并实现MFA。起初,一切都按预期工作。用户可以登录,发送电子邮件没有问题。但是,在某个时候(没有明确的“点”)电子邮件发送功能将停止为该用户工作,并且将在控制台中记录CORS错误。它继续适用于其他登录用户,因此其用户特定。控制台错误:

通过'https://login.microsoftonline.com/xxxxxxx'访问xmlhttprequest'(从 https://www.myemailsendings.com/graphemail/sendemail/sendemail')来自原始'https://www.myemailsendingsite.com'已被CORS策略阻止:no'access-control-gallo-allow-Origin'标题存在于请求的资源上。

我可以找到很多关于类似错误的引用,但是所有这些都是根本无法工作的情况,我感到很困惑。我不明白为什么它起初有效,然后似乎是随机的,它将不再是不再了。目前唯一的解决方案是让用户关闭其浏览器(而不是选项卡)并返回站点并再次登录。我认为正在发生某种超时或有效期,但是我不知道为什么会返回此特定错误。如果Azure AD中需要更改,我将无法访问,因此我需要能够准确地向管理员解释需要做什么。

这是相关的JS和C#:

JavaScript函数:

function sendEmail() {

    if ($("#sendEmailForm").valid()) {
        var formData = new FormData($("#sendEmailForm").get(0));

        var ajax = new XMLHttpRequest();
        ajax.onload = function () {
            if (ajax.status >= 300) {
                toastr.error("Error!");
                console.log(ajax.responseText);
            }
            else {
                toastr.success("Email sent!");
                dt.ajax.reload();
            }
        };
        ajax.open("POST", "/GraphEmail/SendEmail", true);
        ajax.send(formData);
    }
}

C#Controller(电子邮件是我自己的自定义对象):

public class GraphEmailController : Controller
{
    private readonly GraphServiceClient _graphServiceClient;

    public GraphEmailController(IConfiguration configuration,
                        GraphServiceClient graphServiceClient)
    {
        _graphServiceClient = graphServiceClient;
    }

    [HttpPost]
    [AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
    public async Task<IActionResult> SendEmail(Email email)
    {
        string[] toAddresses = email.ToEmail.Split(";", StringSplitOptions.RemoveEmptyEntries);
        string[] ccAddresses;
        List<Recipient> toRecipients = new();
        List<Recipient> ccRecipients = new();

        Message message = new Message();

        foreach (string address in toAddresses)
            toRecipients.Add(new Recipient { 
                EmailAddress = new EmailAddress { 
                    Address = address.Replace(" ", "") 
                } 
            });

        if (!String.IsNullOrWhiteSpace(email.CcEmail))
        {
            ccAddresses = email.CcEmail.Split(";", StringSplitOptions.RemoveEmptyEntries);

            foreach (string address in ccAddresses)
                ccRecipients.Add(new Recipient
                {
                    EmailAddress = new EmailAddress
                    {
                        Address = address.Replace(" ", "")
                    }
                });
        }

        message.ToRecipients = toRecipients;
        message.CcRecipients = ccRecipients;
        message.Subject = email.Subject;
        message.Body = new ItemBody
        {
            Content = email.Body,
            ContentType = BodyType.Html
        };

        if (email.SendTime != null)
        {
            message.SingleValueExtendedProperties = new MessageSingleValueExtendedPropertiesCollectionPage();
            message.SingleValueExtendedProperties.Add(new SingleValueLegacyExtendedProperty()
            {
                Id = "SystemTime 0x3FEF",
                Value = email.SendTime?.AddHours(DateTime.UtcNow.Hour - DateTime.Now.Hour).ToString("o")
            });
        }

        if (email.Attachments != null)
        {

            MessageAttachmentsCollectionPage attachments = new MessageAttachmentsCollectionPage();

            foreach (IFormFile file in email.Attachments)
            {
                if (file.Length > 0)
                {
                    using (var ms = new MemoryStream())
                    {
                        file.CopyTo(ms);
                        var fileBytes = ms.ToArray();
                        string s = Convert.ToBase64String(fileBytes);

                        attachments.Add(new FileAttachment()
                        {
                            ContentBytes = fileBytes,
                            Name = file.FileName
                        });
                    }
                }
            }

            message.Attachments = attachments;
        }

        await _graphServiceClient.Me
            .SendMail(message, true)
            .Request()
            .PostAsync();

        if (!String.IsNullOrWhiteSpace(email.RunsheetEntryIds))
        {
            List<int> runsheetEntryIds = email.RunsheetEntryIds.Split("&rsIds=", StringSplitOptions.RemoveEmptyEntries).Select(Int32.Parse).ToList();

            UpdateStatusAfterEmail(email.IsReminder, runsheetEntryIds);
        }

        return Ok();

    }

}

I have a .NET 6.0 MVC app that uses Microsoft Graph for sending email as the logged in user. I'm using Microsoft Identity to log the user in and implement MFA. At first, it all works as expected. The user can log in, send emails no problem. However, at some point (no clear "point") the emailing sending function will stop working for that user and a CORS error will be recorded in the console. It continues to work for other logged-in users, so its user-specific. The console error:

Access to XMLHttpRequest at 'https://login.microsoftonline.com/XXXXXX' (redirected from https://www.myemailsendingsite.com/GraphEmail/SendEmail') from origin 'https://www.myemailsendingsite.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I can find a lot of references to similar errors, but all of them are the situation where it never works at all and I'm stumped. I don't understand why it works at first and then, seemingly at random, it will just not anymore. The only solution right now, is for the user to close their browser (not the tab) and return to the site and login again. I'm thinking there is some sort of timeout or expiry happening, but I don't know why that would return this particular error. If changes are needed in Azure AD, I don't have access, so I'd need to be able to explain to the admin exactly what needs to be done.

Here is the relevant JS and C#:

Javascript function:

function sendEmail() {

    if ($("#sendEmailForm").valid()) {
        var formData = new FormData($("#sendEmailForm").get(0));

        var ajax = new XMLHttpRequest();
        ajax.onload = function () {
            if (ajax.status >= 300) {
                toastr.error("Error!");
                console.log(ajax.responseText);
            }
            else {
                toastr.success("Email sent!");
                dt.ajax.reload();
            }
        };
        ajax.open("POST", "/GraphEmail/SendEmail", true);
        ajax.send(formData);
    }
}

C# Controller (Email is my own custom object):

public class GraphEmailController : Controller
{
    private readonly GraphServiceClient _graphServiceClient;

    public GraphEmailController(IConfiguration configuration,
                        GraphServiceClient graphServiceClient)
    {
        _graphServiceClient = graphServiceClient;
    }

    [HttpPost]
    [AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
    public async Task<IActionResult> SendEmail(Email email)
    {
        string[] toAddresses = email.ToEmail.Split(";", StringSplitOptions.RemoveEmptyEntries);
        string[] ccAddresses;
        List<Recipient> toRecipients = new();
        List<Recipient> ccRecipients = new();

        Message message = new Message();

        foreach (string address in toAddresses)
            toRecipients.Add(new Recipient { 
                EmailAddress = new EmailAddress { 
                    Address = address.Replace(" ", "") 
                } 
            });

        if (!String.IsNullOrWhiteSpace(email.CcEmail))
        {
            ccAddresses = email.CcEmail.Split(";", StringSplitOptions.RemoveEmptyEntries);

            foreach (string address in ccAddresses)
                ccRecipients.Add(new Recipient
                {
                    EmailAddress = new EmailAddress
                    {
                        Address = address.Replace(" ", "")
                    }
                });
        }

        message.ToRecipients = toRecipients;
        message.CcRecipients = ccRecipients;
        message.Subject = email.Subject;
        message.Body = new ItemBody
        {
            Content = email.Body,
            ContentType = BodyType.Html
        };

        if (email.SendTime != null)
        {
            message.SingleValueExtendedProperties = new MessageSingleValueExtendedPropertiesCollectionPage();
            message.SingleValueExtendedProperties.Add(new SingleValueLegacyExtendedProperty()
            {
                Id = "SystemTime 0x3FEF",
                Value = email.SendTime?.AddHours(DateTime.UtcNow.Hour - DateTime.Now.Hour).ToString("o")
            });
        }

        if (email.Attachments != null)
        {

            MessageAttachmentsCollectionPage attachments = new MessageAttachmentsCollectionPage();

            foreach (IFormFile file in email.Attachments)
            {
                if (file.Length > 0)
                {
                    using (var ms = new MemoryStream())
                    {
                        file.CopyTo(ms);
                        var fileBytes = ms.ToArray();
                        string s = Convert.ToBase64String(fileBytes);

                        attachments.Add(new FileAttachment()
                        {
                            ContentBytes = fileBytes,
                            Name = file.FileName
                        });
                    }
                }
            }

            message.Attachments = attachments;
        }

        await _graphServiceClient.Me
            .SendMail(message, true)
            .Request()
            .PostAsync();

        if (!String.IsNullOrWhiteSpace(email.RunsheetEntryIds))
        {
            List<int> runsheetEntryIds = email.RunsheetEntryIds.Split("&rsIds=", StringSplitOptions.RemoveEmptyEntries).Select(Int32.Parse).ToList();

            UpdateStatusAfterEmail(email.IsReminder, runsheetEntryIds);
        }

        return Ok();

    }

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

魔法少女 2025-02-08 07:59:22

您的描述听起来好像是首次进行登录流,将用户发送到microsoftonline.com,以便与。然后您的功能 sendemail 提供了通过会话cookie认证的成功请求。

一段时间后,会话(cookie)到期,以便您的服务器尝试另一个登录流程吗?只有这次,该请求是一个XMLHTTPRequest,无法重定向到microsoftonline.com Origin。 (尽管初始登录流程在浏览器选项卡中明显发生,而没有交叉原始问题。)

当通过XMLHTTPREQUEST调用而没有有效的会话时,您的服务器不应将您的服务器重定向到microsoftonline.com,而是发送特殊响应,以引起您的特殊响应应用程序可见重复登录流(例如,在另一个浏览器选项卡中)。

Your description sounds as if you first make a logon flow, sending your users to microsoftonline.com in order to establish a session with https://www.myemailsendingsite.com. Then your function sendEmail makes successful requests that are authenticated with a session cookie.

Could it be that, after a while, the session (cookie) expires so that your server attempts another logon flow? Only that, this time, the request is an XMLHttpRequest that cannot be redirected to the microsoftonline.com origin. (Whereas the initial logon flow happened visibly, in a browser tab, with no cross-origin issues.)

When called via XMLHttpRequest without a valid session, your server should not redirect to microsoftonline.com, but instead send a special response that causes your app to repeat the logon flow visibly (in another browser tab, for example).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文