Unable to send email using graph API - microsoft-graph-api

The below code is used to retrieve token
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(clientsec)
.Build();
AuthorizationCodeProvider authprovider = new AuthorizationCodeProvider(confidentialClientApplication, scopes);
//ClientCredentialProvider authprovider = new ClientCredentialProvider(confidentialClientApplication);
var authResult = await confidentialClientApplication
.AcquireTokenForClient(scopes)
.ExecuteAsync().ConfigureAwait(false);
return authResult.AccessToken;
and calling sending mail using Me is not working.
await graphServiceClient.Me
.SendMail(email, false)
.Request()
.PostAsync();
Can someone assist what is the wrong here?
In my scenario I have to send mails to using multiple from addresses.

You are using the ClientCredentials (that means there is no user) so .Me() isn’t available.
To send a mail as a specific user, you’ll need to reference the user.
Here is a sample How that works for teams. https://learn.microsoft.com/en-us/graph/sdks/create-requests?tabs=CS#updating-an-existing-entity-with-patch
var userId = "71766077-aacc-470a-be5e-ba47db3b2e88";
await graphClient.Users[userId]
.SendMail(email, false)
.Request()
.PostAsync();
I’m not sure if you can also use the username (upn) instead of the id.

Related

How do I access Outlook365 mailbox using impersonation using .NET?

I'm using this code:
var cca = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithClientSecret(clientSecret)
.WithTenantId(tenantId)
.Build();
var ewsScopes = new [] { "https://outlook.office365.com/.default" };
var authResult = await cca.AcquireTokenForClient(ewsScopes).ExecuteAsync(cancellationToken);
var service = new ExchangeService
{
Credentials = new OAuthCredentials(authResult.AccessToken),
Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx"),
ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "mailbox#user.com"),
TraceListener = new TraceListener(),
TraceEnabled = true,
TraceFlags = TraceFlags.All
};
Folder inbox = Folder.Bind(service, WellKnownFolderName.Inbox);
The code throws a ServiceRequestException (403) on the last line, and trace logs contains the error:
x-ms-diagnostics: 2000008;reason="The token contains not enough scope to make this call.";error_category="invalid_grant"
Do I need to expand the ewsScopes? Or is this because I'm lacking the correct permissions in Azure? Which roles/permissions do I need?
Check the token your using in
Credentials = new OAuthCredentials(authResult.AccessToken),
in jwt.io
What you should see in the roles is
If you don't have that role it means your application registration isn't correct (eg you have added the delegate permission instead of Application permission which is a common mistake).

using Microsoft graph API i want user profile photo

Hi I am trying to get user photo, used
var tenantId = configuration.GetSection("AzureAd").GetSection("TenantId").Value;
var clientId = configuration.GetSection("AzureAd").GetSection("ClientId").Value;
var clientSecret = configuration.GetSection("AzureAd").GetSection("clientSecret").Value;
var InviteRedirectUrl = configuration.GetSection("AzureAd").GetSection("InviteRedirectUrl").Value;
var Instance = configuration.GetSection("AzureAd").GetSection("Instance").Value;
var URL = Instance + tenantId + "/v2.0";
var scopes = new string[] { "https://graph.microsoft.com/.default" };
var confidentialClient = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithAuthority(URL)
.WithClientSecret(clientSecret)
.Build();
GraphServiceClient graphServiceClient =
new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
{
var authResult = await confidentialClient
.AcquireTokenForClient(scopes)
.ExecuteAsync();
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
})
);
Stream photo = await graphServiceClient.Me.Photo.Content.Request().GetAsync();
I got following error
Code: BadRequest Message: Current authenticated context is not valid for this request. This occurs when a request is made to an endpoint that requires user sign-in. For example, /me requires a signed-in user. Acquire a token on behalf of a user to make requests to these endpoints. Use the OAuth 2.0 authorization code flow for mobile and native apps and the OAuth 2.0 implicit flow for single-page web apps. Inner error:
how to solve it?

Get emails, not MS Teams messages from MS Graph

When I GET https://graph.microsoft.com/v1.0/me/messages, it returns messages I received in MS Teams.
How can I query for only emails using the MS Graph API?
As AAvery said, I believe as well that Teams send you the messages to your Outlook after a while of inactivity. Try to remove those emails(notifications) from MS Teams and try to GET again.
Eventually, try this: https://graph.microsoft.com/v1.0/me/mailFolders/Inbox/messages/delta
I came here to ask same question but I was able to get some clue which helped me to get my code working.
my old code:
public async void ReadMails(IAuthenticationProvider authProvider)
{
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var messages = await graphClient.Me.Messages
.Request()
.Select(e => new
{
e.Sender,
e.Subject,
e.Body
})
.GetAsync();
}
New code code which reads only emails now.
public async void ReadMails(IAuthenticationProvider authProvider)
{
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var messages = await graphClient.Me.MailFolders.Inbox.Messages
.Request()
.Select(e => new
{
e.Sender,
e.Subject,
e.Body
})
.GetAsync();
}
I hope this helps.

Unable to create Planner as a Tab in Microsoft Teams

I have a set of Microsoft Teams that I'm unable to add a Microsoft Planner tab to. When I try and add the Planner I get the dialog and put in the Planner name and click Create and it gives back a Create Plan Failed message. No other information is returned.
This happens doesn't happen in all Microsoft Team, ones that are created normally in the teams app work fine, but ones that I create through the Microsoft Graph have this problem. Here is the code that I'm using to create the team.
public async Task<string> CreateTeam(string title, ClaimsPrincipal user)
{
var userId = user.Claims.First(c => c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var body = $"{{\"displayName\":\"{title}\",\"groupTypes\":[\"Unified\"],\"mailEnabled\":true,\"mailNickname\":\"{title.Replace(" ", "")}\", \"securityEnabled\":false, \"visibility\":\"Private\" }}";
var res = await GraphClient.QueryGraphAsyncPost($"/groups", body, user);
var result = await res.Content.ReadAsStringAsync();
var group = JsonConvert.DeserializeObject<FieldInfoBucket>(result);
var id = group.Id;
res = await GraphClient.QueryGraphAsync($"/groups/{id}/owners", user);
result = await res.Content.ReadAsStringAsync();
body = $"{{\"#odata.id\": \"https://graph.microsoft.com/beta/users/{userId}\"}}";
res = await GraphClient.QueryGraphAsyncPost($"/groups/{id}/owners/$ref", body, user);
// ReSharper disable once RedundantAssignment
result = await res.Content.ReadAsStringAsync();
body =
$"{{\"memberSettings\":{{\"allowCreateUpdateChannels\":true, \"allowDeleteChannels\":true, \"allowAddRemoveApps\":true, \"allowCreateUpdateRemoveTabs\":true, \"allowCreateUpdateRemoveConnectors\":true}}, \"guestSettings\":{{\"allowCreateUpdateChannels\":false, \"allowDeleteChannels\":false}}, \"messageSettings\":{{\"allowUserEditMessages\":true, \"allowUserDeleteMessages\":true, \"allowOwnerDeleteMessages\":true, \"allowTeamMentions\":true, \"allowChannelMentions\":true}},\"funSettings\":{{\"allowGiphy\":true, \"giphyContentRating\":\"strict\",\"allowStickersAndMemes\":true,\"allowCustomMemes\":true}} }}";
res = await GraphClient.QueryGraphAsyncPut($"/groups/{id}/team", body, user);
// ReSharper disable once RedundantAssignment
result = await res.Content.ReadAsStringAsync();
return id;
}
The Graph client above simply issues Get/Post/Put commands against the graph.microsoft.com/beta endpoints and adds the appropriate Bearer token.
Planner is getting confused that its being asked by a user who's not a member of the team. If we add current logged in user (owner) explicitly using /AddMember api then it's workign fine. We are working on the fix.

Store authentification data in MVC

I have created a custom Authorize attribute where I use the Office Graph to get AAD groups the current user is member of, and based on those I reject or authorize the user. I want to save the groups, because the call to Office Graph takes some performance. What would be the correct way to save that kind of data? I can see some people saves it to a SQL server, but then I would need to ensure cleanup etc.
Also I can see in some threads the session state is stated to be a bad choice due to concurrency. So the question is what options do you have to store this kind of information?
All suggestions are welcome.
If you were only using the group_id info, there is no need to use Office Graph and store it at all. We can enable Azure AD issue the groups claims by change the manifest of Azure AD like below:(refer this code sample)
"groupMembershipClaims": "All",
And if you are also using other info about groups, you can store these info into claims. Here is a code sample that add the name of groups into claims for your reference:
AuthorizationCodeReceived = async context =>
{
ClientCredential credential = new ClientCredential(ConfigHelper.ClientId, ConfigHelper.AppKey);
string userObjectId = context.AuthenticationTicket.Identity.FindFirst(Globals.ObjectIdClaimType).Value;
AuthenticationContext authContext = new AuthenticationContext(ConfigHelper.Authority, new TokenDbCache(userObjectId));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(
context.Code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, ConfigHelper.GraphResourceId);
ActiveDirectoryClient graphClient = new ActiveDirectoryClient(new Uri(ConfigHelper.GraphServiceRoot),
async () => { return await Task.FromResult(result.AccessToken); }
);
try
{
foreach (var groupClaim in context.AuthenticationTicket.Identity.FindAll("groups"))
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri($"https://graph.windows.net/adfei.onmicrosoft.com/groups/{groupClaim.Value}?api-version=1.6"),
Method = HttpMethod.Get,
};
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);
using (HttpClient httpClient = new HttpClient())
{
HttpResponseMessage httpResponse = httpClient.SendAsync(request).Result;
var retJSON = httpResponse.Content.ReadAsStringAsync().Result;
var dict = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(retJSON);
((ClaimsIdentity)context.AuthenticationTicket.Identity).AddClaim(new Claim("groupName", dict["displayName"].ToString()));
}
}
}
catch (Exception ex)
{
}
},
Then we can these info from controller using the code below:
ClaimsPrincipal.Current.FindAll("groupName")

Resources