Microsoft Graph API - Mail with Application Only Auth Flow

Nicola Negossi 20 Reputation points
2023-05-09T10:05:43.3066667+00:00

Hello,

I'm not finding any working example, using Microsoft Graph API, to send an email, after correctly getting the App-only token.

Using the GraphServiceClient, I'm getting as result, the request to manually Sign-in as showed here:

"To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code ######## to authenticate."

But I need to implement an automatic mail system, not a system that requires a manual login on every mail send, can you help me?

Func<DeviceCodeInfo, CancellationToken, Task> callback = (code, cancellation) =>
        {
            Console.WriteLine(code.Message);
            return Task.FromResult(0);
        };
        var scopes = new[] { "User.Read" };
        var tenantId = "my_tenant_id";
        var clientId = "my_client_id";
        var options = new TokenCredentialOptions { AuthorityHost = AzureAuthorityHosts.AzurePublicCloud };
        var deviceCodeCredential = new DeviceCodeCredential(callback, tenantId, clientId, options);
        var graphClient = new GraphServiceClient(deviceCodeCredential, scopes);
        var requestBody = new Microsoft.Graph.Me.SendMail.SendMailPostRequestBody
        {
            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 = "nicola.negossi@applied.it",
                },
            },
        },
                CcRecipients = new List<Recipient>
        {
            new Recipient
            {
                EmailAddress = new EmailAddress
                {
                    Address = "alessandra.tavani@applied.it",
                },
            },
        },
            },
            SaveToSentItems = false,
        };
        await graphClient.Me.SendMail.PostAsync(requestBody);
Microsoft Graph
Microsoft Graph
A Microsoft programmability model that exposes REST APIs and client libraries to access data on Microsoft 365 services.
11,447 questions
0 comments No comments
{count} votes

Accepted answer
  1. TH-4749-MSFT 3,295 Reputation points
    2023-05-09T14:53:54.08+00:00

    Hello Nicola Negossi,

    Thanks for reaching out. You can achieve your requirement using the ClientSecretCredentials method. You can find more information and example in this article.

    Please note that 'graphClient.Me.SendMail.PostAsync(requestBody)' used with Application context created via the ClientSecretCredentials method will throw an error. As the application context does not have a "user" that the application is acting as, you cannot call the /me endpoint. To do this you would need to acquire a user token so that the /me call is associated with that user.

    To do this, you would need to authenticate using one of the public client flows that involves acquiring tokens for a user. Please see GitHub forum post for more details on the topic.

    You can however use the'graphClient.Users[userID/UPN].SendMail.PostAsync(requestBody)' where variable 'requestBody' is of type 'Microsoft.Graph.Users.Item.SendMail.SendMailPostRequestBody'.

    If the reply is helpful, please click Accept Answer and kindly upvote it. If you have additional questions about this answer, please click Comment.

    Thanks,

    2 people found this answer helpful.

2 additional answers

Sort by: Most helpful
  1. TH-4749-MSFT 3,295 Reputation points
    2023-05-29T16:14:37.5533333+00:00

    Hi Nicola Negossi,

    Thanks for your response. Please decode the token using https://jwt.ms and verify if the required roles are present in the token. Based on the example you provided the code provided seems to be valid and may require further investigation. I would suggest opening a support ticket or create a post for the issue in the Graph Github forum https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues.

    Thanks.

    0 comments No comments

  2. Nicola Negossi 20 Reputation points
    2023-05-15T12:54:43.23+00:00

    Hi TH-4749,

    I'm still far from the end, I've tried the following, but getting an Microsoft.Graph.Models.ODataErrors.ODataError: Exception of type 'Microsoft.Graph.Models.ODataErrors.ODataError,
    and I'm not sure if I'm making errors on the request body, or something else.

    Probably we're using the wrong approach...

    We've registered on Azure, an account [let's call it "svc23xxx@bosscompany.it"].

    It has a shared "fully permissioned" account that we "MySharedAccount@mycompany.it".

    In the code below, I've used directly "MySharedAccount@mycompany.it" and not "svc23xxx@bosscompany.it", so probably that could be the mistake.

    But at this point I'm wondering if we are still over the right path.

    The objective is to send an email automatically from a Windows Daemon Service, a daily mail from the Account "MySharedAccount@mycompany.it", without any human interaction.

    public async static Task MakeGraphCallAsync()
    
        {
    
            // The client credentials flow requires that you request the
    
            // /.default scope, and preconfigure your permissions on the
    
            // app registration in Azure. An administrator must grant consent
    
            // to those permissions beforehand.
    
            var scopes = new[] { "https://graph.microsoft.com/.default" };
    
    
    
            // Multi-tenant apps can use "common",
    
            // single-tenant apps must use the tenant ID from the Azure portal
    
            var tenantId = "here_my_tenant";
    
    
    
            // Values from app registration
    
            var clientId = "here_my_client”;
    
            var clientSecret = "here_my_clientsecret";
    
    
    
            // using Azure.Identity;
    
            var options = new TokenCredentialOptions
    
            {
    
                AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
    
            };
    
    
    
            // https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
    
            var clientSecretCredential = new ClientSecretCredential(
    
                tenantId, clientId, clientSecret, options);
    
    
    
            var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
    
            var user = graphClient.Users["MySharedAccount@mycompany.it"];
    
            Microsoft.Graph.Users.Item.SendMail.SendMailPostRequestBody requestBody = new Microsoft.Graph.Users.Item.SendMail.SendMailPostRequestBody
    
            {
    
                Message = new Message
    
                {
    
                    Subject = "Test Mail",
    
                    Body = new ItemBody
    
                    {
    
                        ContentType = BodyType.Text,
    
                        Content = "Example Body",
    
                    },
    
                    ToRecipients = new List<Recipient>
    
            {
    
                new Recipient
    
                {
    
                    EmailAddress = new EmailAddress
    
                    {
    
                        Address = "nicola.surname@mycompany.it",
    
                    },
    
                },
    
            },
    
                    CcRecipients = new List<Recipient>
    
            {
    
                new Recipient
    
                {
    
                    EmailAddress = new EmailAddress
    
                    {
    
                        Address = "alessandra.surname@mycompany.it",
    
    
    
                    },
    
                },
    
            },
    
                },
    
                SaveToSentItems = false,
    
            };
    
            try
    
            {
    
                await graphClient.Users["MySharedAccount@applied.it"].SendMail.PostAsync(requestBody);
    
            }
    
            catch (Exception ex)
    
            {
    
                System.Console.WriteLine(ex);
    
            }
    
        }
    
    0 comments No comments