简体   繁体   中英

C# Microsoft Graph - How to send email with access token from msal-browser

I'm using C# Microsoft Graph to send an email but I'm facing the error "Object reference not set to an instance of an object" when I call the await graphClient.Me.SendMail(message).Request().PostAsync() method. I tried to call var request = graphClient.Me.SendMail(message).Request() first and see that the request object is not null but when I call request.PostAsync() it gives the error, so I think PostAsync() method has a problem or I am missing something.

I'm not sure that if we can use the access token from msal-browser to send an email in C#?

Here is the code workflow:

  1. On the browser I call msalInstance.loginPopup(loginRequest) method to get accesstoken after user selects their outlook account.
  2. Then I send the accesstoken from the client to the backend to send an email. But I'm facing the error at this step await graphClient.Me.SendMail(message).Request().PostAsync();

Notes: I want to send an email from the backend (C#) instead of javascript because I want to handle some logic code at the backend.

Here is SDK: https://alcdn.msauth.net/browser/2.7.0/js/msal-browser.min.js and Microsoft.Graph version 1.21.0

Here is javascript code:

var msalConfig = {
    auth: {
        clientId: "clientId",
        authority: "https://login.microsoftonline.com/{tenantId}",
        redirectUri: location.origin
    }
};
    
var msalInstance = new msal.PublicClientApplication(msalConfig);
    
var loginRequest = {
    scopes: ["User.Read", "Mail.Send"]
};
msalInstance.loginPopup(loginRequest)
    .then((res) => {
        methodCallAPIFromDotNet(res.accessToken); // This is method is used to call API from C# with the accessToken
    })
    .catch(error => {
        console.error(error);
    });

Here is C# code:

public async Task<bool> Send(string accessToken)
        {
            var message = new Message
            {
                Subject = "Meet for lunch?",
                Body = new ItemBody
                {
                    ContentType = BodyType.Text,
                    Content = "The new cafeteria is open."
                },
                ToRecipients = new List<Recipient>
                {
                    new Recipient
                    {
                        EmailAddress = new EmailAddress
                        {
                            Address = "to email address"
                        }
                    }
                },
                Attachments = new MessageAttachmentsCollectionPage()
            };

            var authProvider = new DelegateAuthenticationProvider(requestMessage =>
            {
                requestMessage.Headers.Authorization =
                    new AuthenticationHeaderValue("Bearer", accessToken);
                return System.Threading.Tasks.Task.FromResult(0);
            });

            var graphClient = new GraphServiceClient(authProvider);

            var me = await graphClient.Me.Request().GetAsync(); // I can get user info here.

            await graphClient.Me.SendMail(message).Request().PostAsync(); // Error here
            return true;
        }

Here is exception:

at Microsoft.Graph.HttpProvider.d__18.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Graph.BaseRequest.d__35.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Graph.BaseRequest.d__30.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()

You used var graphClient = new GraphServiceClient(authProvider); to initial the graphClient , and the access token is passed from the frontend, so I think you followed this sample to use on-behalf-flow here. But the code snippet you provided is different here. So I think this is the reason for the issue about sending email failed.

Here you have 2 options, one is read about the on-behalf-flow and if you accept to use the flow here, you just need to follow the tutorial I provided above to complete the code and try.

But I think you may prefer the second way. In my opinion, what you need your backend do is executing some logic to complete the email, then you call the api to send the email. If so, why not sending http request directly as you've got the access token.

You may check if the access token worked for sending email, just try calling api with the token in some tools like postman. And if you got 202 response, that means the token is ok.

在此处输入图像描述

Then you need to modify your send email method like this, it worked for me:

    public async Task sendAsync() {
                var mesg = new Message
                {
                    Subject = "Meet for lunch?",
                    Body = new ItemBody
                    {
                        ContentType = BodyType.Text,
                        Content = "The new cafeteria is open."
                    },
                    ToRecipients = new List<Recipient>
                    {
                        new Recipient
                        {
                            EmailAddress = new EmailAddress
                            {
                                Address = "tiny-wa@outlook.com"
                            }
                        }
                    },
                    Attachments = new MessageAttachmentsCollectionPage()
                };
                var temp = new MailContentModel
                {
                    message = mesg
                };
                var jsonStr = JsonSerializer.Serialize(temp);
                string token = "your_token";
                var httpClient = new HttpClient();
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
                HttpResponseMessage response = await httpClient.PostAsync("https://graph.microsoft.com/v1.0/me/sendMail", new StringContent(jsonStr, Encoding.UTF8, "application/json"));
            }

using Microsoft.Graph;

public class MailContentModel
    {
        public Message message { get; set; }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM