One of the new features added to the Microsoft Graph recently was the ability to create and send Mime Messages (you have been able to get Message as Mime for a while). This is useful in a number of different scenarios especially when trying to create a Message with inline Images which has historically been hard to do with both the Graph and EWS (if you don't use MIME). It also opens up using SMIME for encryption and a more easy migration path for sending using SMTP in some apps.
MimeKit is a great open source library for parsing and creating MIME messages so it offers a really easy solution for tackling this issue. The current documentation on Send message via MIME lacks any real sample so I've put together a quick console app that use MSAL, MIME kit and the Graph SDK to send a Message via MIME. As the current Graph SDK also doesn't support sending via MIME either there is a workaround for this in the future my guess is this will be supported.
using System; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using System.Net.Http; | |
using System.Net.Http.Headers; | |
using Microsoft.Identity.Client; | |
using Newtonsoft.Json; | |
using Microsoft.Graph; | |
using System.IO; | |
using MimeKit; | |
using MimeKit.Utils; | |
namespace GraphMimeKitMSAL | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
string mailboxName = "user@domain.com"; | |
String recipient = "target@domain.com"; | |
string scope = "https://graph.microsoft.com/.default"; | |
string redirectUri = "http://localhost"; | |
string clientId = "5471030d-f311-4c5d-91ef-74ca885463a7"; | |
string inlineImagePath = @"C:\temp\test.jpg"; | |
// Code attribution | |
// The following MIMEkit code is a modified version of the sample included on | |
// http://www.mimekit.net/docs/html/Working-With-Messages.htm | |
var message = new MimeMessage(); | |
message.From.Add(MailboxAddress.Parse(mailboxName)); | |
message.To.Add(MailboxAddress.Parse(recipient)); | |
message.Subject = "How you doin?"; | |
// create our message text, just like before (except don't set it as the message.Body) | |
var builder = new BodyBuilder(); | |
// Set the plain-text version of the message text | |
builder.TextBody = @"Hey Alice, | |
What are you up to this weekend? Monica is throwing one of her parties on | |
Saturday and I was hoping you could make it. | |
Will you be my +1? | |
-- Joey | |
"; | |
// In order to reference inline image from the html text, we'll need to add it | |
// to builder.LinkedResources and then use its Content-Id value in the img src. | |
var image = builder.LinkedResources.Add(inlineImagePath); | |
image.ContentId = MimeUtils.GenerateMessageId(); | |
// Set the html version of the message text | |
builder.HtmlBody = string.Format(@"<p>Hey Alice,<br> | |
<p>What are you up to this weekend? Monica is throwing one of her parties on | |
Saturday and I was hoping you could make it.<br> | |
<p>Will you be my +1?<br> | |
<p>-- Joey<br> | |
<center><img src=""cid:{0}""></center>", image.ContentId); | |
// Now we just need to set the message body and we're done | |
message.Body = builder.ToMessageBody(); | |
//End MimeKit Sample | |
var stream = new MemoryStream(); | |
message.WriteTo(stream); | |
stream.Position = 0; | |
StringContent MessagePost = new StringContent(Convert.ToBase64String(stream.ToArray()),Encoding.UTF8, "text/plain"); | |
GraphServiceClient graphServiceClient = new GraphServiceClient(new AuthHelp(redirectUri, scope, mailboxName, clientId)); | |
var Message = new Message { }; | |
var sendMailRequest = graphServiceClient.Me.SendMail(Message, null).Request().GetHttpRequestMessage(); | |
sendMailRequest.Content = MessagePost; | |
sendMailRequest.Method = HttpMethod.Post; | |
var sendResult = graphServiceClient.HttpProvider.SendAsync(sendMailRequest).Result; | |
Console.WriteLine(sendResult.StatusCode); | |
} | |
} | |
class AuthHelp : IAuthenticationProvider | |
{ | |
internal Microsoft.Identity.Client.PublicClientApplication PublicClientApplication { get; set; } | |
private string Scope { get; set; } | |
public AuthHelp(String redirectUri,string scope,string mailboxName,string clientId) | |
{ | |
Scope = scope; | |
HttpClient httpclient = new HttpClient(); | |
var TenantId = ((dynamic)JsonConvert.DeserializeObject(httpclient.GetAsync("https://login.microsoftonline.com/" + mailboxName.Split('@')[1] + "/v2.0/.well-known/openid-configuration") | |
.Result.Content.ReadAsStringAsync().Result)) | |
.authorization_endpoint.ToString().Split('/')[3]; | |
PublicClientApplicationBuilder pcaConfig = PublicClientApplicationBuilder.Create(clientId) | |
.WithTenantId(TenantId); | |
pcaConfig.WithRedirectUri(redirectUri); | |
PublicClientApplication = (Microsoft.Identity.Client.PublicClientApplication)pcaConfig.Build(); | |
var tokenResult = PublicClientApplication.AcquireTokenInteractive(new[] { scope }) | |
.WithPrompt(Microsoft.Identity.Client.Prompt.SelectAccount) | |
.WithLoginHint(mailboxName).ExecuteAsync().Result; | |
} | |
public Task AuthenticateRequestAsync(HttpRequestMessage request) | |
{ | |
var accounts = PublicClientApplication.GetAccountsAsync().Result; | |
IAccount account = accounts.FirstOrDefault(); | |
AuthenticationResult authResult = PublicClientApplication.AcquireTokenSilent(new[] { Scope }, account).ExecuteAsync().Result; | |
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", authResult.AccessToken); | |
return System.Threading.Tasks.Task.CompletedTask; | |
} | |
} | |
} |