0
votes

I am trying to add a large attachment to an email using Microsoft Graph.

Steps:

Get Token:

public static async Task<GraphServiceClient> GetAuthenticatedClientForApp(IConfidentialClientApplication app)
{
    GraphServiceClient graphClient = null;
    // Create Microsoft Graph client.
    try
    {
        var token = await GetTokenForAppAsync(app);
        graphClient = new GraphServiceClient(
            "https://graph.microsoft.com/beta",
            new DelegateAuthenticationProvider(async(requestMessage) =>
            {
                requestMessage.Headers.Authorization =
                    new AuthenticationHeaderValue("bearer", token);

            }));
        return graphClient;
    }

    catch (Exception ex)
    {
        Logger.Error("Could not create a graph client: " + ex.Message);
    }

    return graphClient;
}
/// <summary>
/// Get Token for App.
/// </summary>
/// <returns>Token for app.</returns>
public static async Task<string> GetTokenForAppAsync(IConfidentialClientApplication app)
{
    AuthenticationResult authResult;
    authResult = await app
        .AcquireTokenForClient(new string[] { "https://graph.microsoft.com/.default" })
        .ExecuteAsync(System.Threading.CancellationToken.None);
    return authResult.AccessToken;
}

Create Draft:

Message draft = await client
    .Users[emailDTO.FromEmail]
    .Messages
    .Request()
    .AddAsync(msg);

Attach file:

if (emailDTO.FileAttachments != null && emailDTO.FileAttachments.Count() > 0)
{
    foreach (EmailAttachment emailAttachment in emailDTO.FileAttachments)
    {
        if (emailAttachment.UploadFile != null && emailAttachment.UploadFile.Length > 0)
        {
            var attachmentItem = new AttachmentItem
            {
                AttachmentType = AttachmentType.File,
                Name = emailAttachment.FileName,
                Size = emailAttachment.UploadFile.Length
            };
            var session = await client
                .Users[emailDTO.FromEmail]
                .MailFolders
                .Drafts
                .Messages[draft.Id]
                .Attachments
                .CreateUploadSession(attachmentItem)
                .Request()
                .PostAsync();

            var stream = new MemoryStream(emailAttachment.UploadFile);
            var maxChunkSize = 320 * 1024 * 1024;
            var provider = new ChunkedUploadProvider(session, client, stream, maxChunkSize);
            var readBuffer = new byte[maxChunkSize];
            var chunkRequests = provider.GetUploadChunkRequests();

            //var uploadedItem = await provider.UploadAsync();
            var trackedExceptions = new List<Exception>();
            foreach (var rq in chunkRequests)
            {
                var result = await provider.GetChunkRequestResponseAsync(rq, readBuffer, trackedExceptions);
            }
        }
    }
}

Error:

{
  Code: InvalidAudienceForResource
  Message: The audience claim value is invalid for current resource. 
            Audience claim is 'https://graph.microsoft.com', request url is
            'https://outlook.office.com/api/beta/User
2
Have scopes have you registered for? It would also be helpful to see the entire error and the actual token you've received.Marc LaFleur
Marc, does graph app needs this Exchange permissions Mail.ReadWrite Application Read and write mail in all mailboxes. It did had the graph > Mail.ReadWrite Permission.user258427
This was the solution for me docs.microsoft.com/en-us/graph/sdks/…Sn3akyP3t3

2 Answers

1
votes

I believe the problem here is that the session URL that gets created points to a resource that is not on Microsoft Graph. However, when you use the same client to call that endpoint it passes the bearer token that belongs to Graph. I believe the session URL has an access token in the URL that is sufficient.

You could update your DelegateAuthenticationProvider function to only add the Authorization header for hosts that are graph.microsoft.com. Or you could use our LargeFileUploadTask instead of the ChunkedUploadProvider and it will do much of this work for you. Sadly, I haven't finished the docs for it yet. I'll come back and update this post soon with a docs link.

0
votes
var task = new Task(() =>
{
    foreach(var attachment in attachments) {
        using(MemoryStream stream = new MemoryStream()) {

            var mimePart = (MimePart)attachment;
            mimePart.Content.DecodeTo(stream);
            var size = MeasureAttachmentSize(mimePart);
            var attachmentItem = MapAttachmentItem(attachment, size);

            // Use createUploadSession to retrieve an upload URL which contains the session identifier.
            var uploadSession = client.Users[mailbox]
                .Messages[addedMessage.Id]
                .Attachments
                .CreateUploadSession(attachmentItem)
                .Request()
                .PostAsync()
                .GetAwaiter()
                .GetResult();

            // Max slice size must be a multiple of 320 KiB
            int maxSliceSize = 320 * 1024;
            var fileUploadTask = new LargeFileUploadTask<FileAttachment>(uploadSession
                                                                        ,stream
                                                                        ,maxSliceSize
                                                                        ,client);

            // Create a callback that is invoked after each slice is uploaded
            IProgress<long> progress = new Progress<long>(prog =>
            {
                Console.WriteLine($"Uploaded {prog} bytes of {stream.Length} bytes");
            });

            try {
                // Upload the file
                var uploadResult = fileUploadTask.UploadAsync(progress, 3).Result;

                if(uploadResult.UploadSucceeded) {
                    // The result includes the location URI.
                    Console.WriteLine($"Upload complete, LocationUrl: {uploadResult.Location}");
                }
                else {
                    Console.WriteLine("Upload failed");
                }
            }
            catch(ServiceException ex) {
                Console.WriteLine($"Error uploading: {ex.ToString()}");
                throw ex;
            }
        }
    }
});
task.RunSynchronously();