Unable to send email using Mail.Send Delegated Permissions - c#

I created a C# console application to send email using Microsoft Graph API. On adding Mail.Send Delegated Permission to my application, I see the following exception:
I have enabled 'Allow public client flows':
The application has Mail.Send permission:
Here is my code:
public async Task SendMail(string subject, string content, string recipientAddress)
{
var publicClientApplication = PublicClientApplicationBuilder
.Create("<client id>")
.WithTenantId("<tenant id>")
.Build();
string[] scopes = new string[] { "mail.send" };
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var message = new Message
{
Subject = subject,
Body = new ItemBody
{
ContentType = BodyType.Text,
Content = content
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress { Address = recipientAddress }
}
}
};
var securePassword = new SecureString();
foreach (char c in _senderPassword)
securePassword.AppendChar(c);
var saveToSentItems = true;
await graphClient.Me
.SendMail(message, saveToSentItems)
.Request().WithUsernamePassword(_senderAddress, securePassword)
.PostAsync();
}
What am I missing?

You need to meet the following points:
You must have Mail.Send delegation permissions, you can use jwt.ms to parse your access token to view scp claims:
2.Ensure that your account has an Exchange online license under O365 subscription. See: assign licenses to one user.
My code for your reference:
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace devicecode
{
class Program
{
static async Task Main(string[] args)
{
string graphScope = "User.Read User.ReadBasic.All Mail.Send Mail.Send.Shared";
var graphScopes = graphScope.Split(' ').ToArray();
// Build a client application.
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create("My clienid")
.Build();
DeviceCodeProvider authProvider = new DeviceCodeProvider(publicClientApplication, graphScopes);
// Create an authentication provider by passing in a client application and graph scopes.
// Create a new instance of GraphServiceClient with the authentication provider.
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
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 = "mytestaccount"
}
}
}
};
var saveToSentItems = false;
await graphClient.Me
.SendMail(message, saveToSentItems)
.Request()
.PostAsync();
}
}
}
print:

Related

Creating a user for Azure AD B2C app, how to acquire token correctly? Failing with "parsing_wstrust_response_failed"

[SOLVED, see the edits]
I am working in Linqpad 6, running a script that I made based on the following articles:
https://learn.microsoft.com/en-us/graph/api/user-post-users?view=graph-rest-1.0&tabs=csharp
https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS
Here is my script:
void Main()
{
Debug.WriteLine("yo");
UserCreator creator = new();
creator.CreateUser();
}
public class UserCreator
{
public async void CreateUser()
{
var scopes = new[] { "User.ReadWriteAll" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "<MY_TENANT_ID>";
// Value from app registration
var clientId = "<MY_APPLICATION_ID>";
var pca = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.Build();
// DelegateAuthenticationProvider is a simple auth provider implementation
// that allows you to define an async function to retrieve a token
// Alternatively, you can create a class that implements IAuthenticationProvider
// for more complex scenarios
var authProvider = new DelegateAuthenticationProvider(async (request) =>
{
// Use Microsoft.Identity.Client to retrieve token
var result = await pca.AcquireTokenByIntegratedWindowsAuth(scopes).ExecuteAsync();
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
});
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = new User
{
AccountEnabled = true,
DisplayName = "John",
MailNickname = "John",
UserPrincipalName = "john#mail.com",
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = "xWwvJ]6NMw+bWH-d"
}
};
await graphClient.Users
.Request()
.AddAsync(user);
}
}
I am trying to add a new user to an Azure AD B2C app, but the request is failing with an InnerException of:
"The system cannot contact a domain controller to service the authentication request."
I am suspecting I need more info for the script, such as the name of the registered App, but I cannot find anything about it in the documentation. I find it likely that the request is not returning the correct auth token.
Below is a screenshot of the error:
Updated code
This is my final, working result. Originally, I tried to create a user through my own account, but MFA got in the way. The actual way to do it, is through an app registration.
void Main()
{
UserCreator creator = new();
creator.CreateUser();
}
public class UserCreator
{
public async void CreateUser()
{
var clientId = "<CLIENT_ID>";
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "<TENANT_ID>";
var clientSecret = "<CLIENT_SECRET>";
// 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 = new{
Email = "test#mail.dk",
DisplayName = "TestUser",
Username = "Someusername",
};
var invitation = new Invitation
{
InvitedUserEmailAddress = user.Email,
InvitedUser = new User
{
AccountEnabled = true,
DisplayName = "TestUser",
CreationType = "LocalAccount",
PasswordPolicies = "DisableStrongPassword",
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = "Test123456",
}
},
InvitedUserType = "member",
SendInvitationMessage = true,
InviteRedirectUrl = "someurl.com"
};
await graphClient.Invitations
.Request()
.AddAsync(invitation);
Console.Write("completed");
}
}
Try to set Azure AD authority by .WithAuthority instead of WithTenantId.
There is a typo in your scopes. Required permission is User.ReadWrite.All not User.ReadWriteAll.
var scopes = new[] { "User.ReadWrite.All" };
...
var pca = PublicClientApplicationBuilder
.Create(clientId)
.WithAuthority($"https://login.microsoftonline.com/{tenantId}")
.WithDefaultRedirectUri()
.Build();

Microsoft.Graph - QueryOptions

After installing the Microsoft Graph SDK and installing the NuGet package in my application when I add using Microsoft.Graph and trying to make a list of QueryOption, it doesn't even pick it up.
My QueryOption just stays red and there is nothing that it suggests me to add.
using Azure.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph.Core;
using Microsoft.Graph;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
public GraphServiceClient showCalendar()
{
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "common";
var clientId = "xxx";
var clientSecret = "xxx";
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential,scopes);
return graphClient;
}
public void CalendarViews()
{
var graphClient = showCalendar();
var options = new List<QueryOption>()
{
new QueryOption("startDateTime", "2021-06-01T00:00:00.0000000"),
new QueryOption("endDateTime", "2021-06-30T23:59:59.9999999")
};
var events = graphClient.Me.CalendarView.Request(options).GetAsync().Result;
}
}
The parts where are QueryOption and Request are not getting picked put and I can't call them.

Unable to show Response Header data from Post call to a Textbox c#

I have create a c# custom application to create chat using one-on-one chat using user principal name. I want to show the response header into the Textbox when the user click the create button. I have follow the article below Get Response Header data from Post Call to get the response but currently I'm having some errors.
Coding part of Create Chat
private async void button2_Click(object sender, EventArgs e)
{
var scopes = new[] { "Directory.Read.All", "Directory.ReadWrite.All", "User.Read", "User.Read.All", "User.ReadBasic.All", "User.ReadWrite" };
{
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "5exxxx3-376a-43b1-9xxx0c-ef3xxxf8bx0";
// Value from app registration
var clientId = "35xxx-5c92-4xxx9-8500-635xxxe8af";
// using Azure.Identity;
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var userName = "ng.xxxxxi.com";
var password = "Aaxxxxxx";
// https://learn.microsoft.com/dotnet/api/azure.identity.usernamepasswordcredential
var userNamePasswordCredential = new UsernamePasswordCredential(
userName, password, tenantId, clientId, options);
GraphServiceClient graphClient = new GraphServiceClient(userNamePasswordCredential, scopes);
var chat = new Chat
{
ChatType = ChatType.OneOnOne,
Members = new ChatMembersCollectionPage()
{
new AadUserConversationMember
{
Roles = new List<String>()
{
"owner"
},
AdditionalData = new Dictionary<string, object>()
{
{"user#odata.bind", "https://graph.microsoft.com/v1.0/users[comboBox2.Text]"}
}
},
new AadUserConversationMember
{
Roles = new List<String>()
{
"owner"
},
AdditionalData = new Dictionary<string, object>()
{
{"user#odata.bind", "https://graph.microsoft.com/v1.0/users/[comboBox3.Text]"}
}
}
}
};
var requestUrl = graphClient.Chats.Request().RequestUrl;
var content = "json_content";
var hrm = new HttpRequestMessage(HttpMethod.Post, requestUrl);
hrm.Content = new StringContent(content, System.Text.Encoding.UTF8, "aplication/json");
// Authenticate (add access token)
await graphClient.AuthenticationProvider.AuthenticateRequestAsync(hrm);
// Send the request and get the response.
var response = await graphClient.HttpProvider.SendAsync(hrm);
if (!response.IsSuccessStatusCode)
{
throw new ServiceException(
new Error
{
Code = response.StatusCode.ToString(),
Message = await response.Content.ReadAsStringAsync()
});
}
else
{
// read header values
var headerValues = response.Headers.GetValues(textBox2.Text);
}
await graphClient.Chats
.Request()
.AddAsync(chat);
}
}

Unable to post inline images along with the message in Microsoft Teams via Microsoft Graph API using C#

I have been trying to post the images along with the message in Microsoft Teams via MS Graph API using C# but unable to do so.
Below is the code I have tried:
string userName = ConfigurationManager.AppSettings["UserName"];
string password = ConfigurationManager.AppSettings["Password"];
System.Security.SecureString passWordSecureString = new System.Security.SecureString();
foreach (char c in password.ToCharArray()) passWordSecureString.AppendChar(c);
var clientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
var tenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
string[] scopes = { "ChannelMessage.Send", "Group.ReadWrite.All", "User.Read" };
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.Build();
//creating the graph user context.
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, scopes);
var chatMessage = new ChatMessage
{
Subject = null,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = "Hello world </br><div><div>\n<div><span><img height=\"297\" src=\"../hostedContents/1/$value\" width=\"297\" style=\"vertical-align:bottom; width:297px; height:297px\"></span>\n\n</div>\n\n\n</div>\n</div>"
},
};
ChatMessageHostedContent chatMessageHostedContent = new ChatMessageHostedContent
{
ContentBytes = Encoding.ASCII.GetBytes("iVBORw0KGgoAAAANSUhEUgAAASkA..."),
ContentType = "image/png",
AdditionalData = new Dictionary<string, object>()
{
{"#microsoft.graph.temporaryId", "1"}
}
};
IChatMessageHostedContentsCollectionPage chatMessageHostedContentsCollectionPage = new ChatMessageHostedContentsCollectionPage();
chatMessageHostedContentsCollectionPage.Add(chatMessageHostedContent);
if (chatMessageHostedContentsCollectionPage.Count > 0)
chatMessage.HostedContents = chatMessageHostedContentsCollectionPage;
ChatMessage sentMessage = await graphClient.Teams["{id}"].Channels["{id}"].Messages
.Request()
.AddAsync(chatMessage);
The image is getting uploaded but it's not showing anything i.e. it's broken/corrupted as shown below:
I have even tried the below code to add an image to the sent message but its throwing an "unknown error" exception.
await graphClient.Teams["{id}"].Channels["{id}"].Messages[sentMessage.Id].HostedContents
.Request()
.AddAsync(chatMessageHostedContent);
I am able to post the same image along with text successfully using the Post HTTP method in MS Graph Explorer with below URL & payload.
https://graph.microsoft.com/beta/teams/{id}/channels/{id}/messages
{"body":{"contentType":"html","content":"Hello world </br><div><div>\n<div><span><img height=\"297\" src=\"../hostedContents/1/$value\" width=\"297\" style=\"vertical-align:bottom; max-width: 100%; height:auto\"></span>\n\n</div>\n\n\n</div>\n</div>"},"hostedContents":[{"#microsoft.graph.temporaryId":"1","contentBytes":"iVBORw0KGgoAAAANSUhEUgAAASkAAAEpCA...","contentType":"image/png"}]}
Can anybody suggest what is wrong in the code?

Microsoft Graph API unable to Send Email C# Console

I have created a small Console App to send email using Microsoft Graph API.
Tutorial Used
https://learn.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=csharp
Error
ServiceException: Code: NoPermissionsInAccessToken Message: The token
contains no permissions, or permissions can not be understood.
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Graph.Extensions;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
namespace GraphAPI
{
class Program
{
static void Main(string[] args)
{
// Azure AD APP
string clientId = "<client Key Here>";
string tenantID = "<tenant key here>";
string clientSecret = "<client secret here>";
Task<GraphServiceClient> callTask = Task.Run(() => SendEmail(clientId, tenantID, clientSecret));
// Wait for it to finish
callTask.Wait();
// Get the result
var astr = callTask;
}
public static async Task<GraphServiceClient> SendEmail(string clientId, string tenantID, string clientSecret)
{
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
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 = "myToEmail#gmail.com"
}
}
},
CcRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = "myCCEmail#gmail.com"
}
}
}
};
var saveToSentItems = true;
await graphClient.Me
.SendMail(message, saveToSentItems)
.Request()
.PostAsync();
return graphClient;
}
}
}
Here is the Screenshot of permissions I gave to the AD APP
So, Can anybody guide me where I am going wrong
Based on your screenshot, you haven't grant admin consent to Mail.Send application permission.
Click the grant admin consent button under api permissions.
Update:
Interactive provider:
string[] scopes = { "Mail.Send" };
string clientId = "";
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(clientId)
.WithRedirectUri("https://localhost")
.Build();
InteractiveAuthenticationProvider authProvider = new InteractiveAuthenticationProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);

Categories

Resources