I have the following query to retrieve a User using the GraphServiceClient with .net core 2.
user = await _graphClient.Users[principalName].Request()
.Expand("Extensions")
.GetAsync();
When I run this I get the following error
Microsoft.Graph.ServiceException: Code: generalException Message:
Unexpected exception returned from the service.
This only happens when I have added a OpenTypeExtension to the user using the following code!
extension = new OpenTypeExtension
{
ExtensionName = AzureADExtensions.UserConstants.ExtensionName,
AdditionalData = new Dictionary<string, object>
{
{"OtherEmail", externalUser.Email},
{"OtherRole" , externalUser.Roles.FirstOrDefault()}
}
};
await _graphClient.Users[user.Id].Extensions.Request()
.AddAsync(extension);
I am starting to get really getting fed up with Azure AD now.
I can't seem to add any meta data to my users. Only doing this because otherEmails does not work with the GraphServiceClient and trying to use any other sensible fields gives me this error:
Tenant does not have a SPO license when updating user
Any help would be appreciated.
So for anyone else that come across this, it seems to be an a bug in the service client.
The code below fails
user = await _graphClient
.Users[userId]
.Request()
.Expand("Extensions")
.GetAsync();
But this code works, just by requesting the extensions in a separate call!
user = await _graphClient
.Users[userId]
.Request()
.GetAsync();
var extensions = await _graphClient.Users[user.Id].Extensions.Request().GetAsync();
user.Extensions = extensions;
Hope that save's someone the time I lost on this!
Related
I'm trying a to add query options to my graphclient. According to the documentation, this should be possible. However, I always get the message that the request property is no longer present in the Microsoft.Graph nuget. Which is strange. Downgrading to older versions does not help.
I'm trying to do something like this:
var options = new List<QueryOption>
{
new QueryOption("$select=internetMessageHeaders")
}
var mimeContent = await graphClient
.Users[TestProperties.UserEmail]
.Messages[specificMailResponse.Id]
.Request(options)
.GetAsync();
Can anyone tell me what I am doing wrong and why the request property no longer exists?
I'm using Microsoft.Graph 5.0.0-preview.10
At the end of the documentation my example is described https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/dev/docs/upgrade-to-v4.md
I followed this page to setup a MicrosoftGraphProvider: http://www.keithmsmith.com/get-started-microsoft-graph-api-calls-net-core-3/
This is working correctly, as I am able to get a list of all of my users with the following request.
var user = await _graphServiceClient.Users.Request().GetAsync();
However, I don't always want all of the users returned, so I have a filter on a user by email.
The example says to do this
var user = await _graphServiceClient.Users[email].Request().GetAsync();
But this always results in user not found, even if I pass a valid email from the response of all users.
So I tried to build a filter, and do it this way.
var test = await _graphServiceClient.Users["$filter=startswith(mail,'test#email.com')"].Request().GetAsync();
var test = await _graphServiceClient.Users["$filter=(startswith(mail,'test#email.com'))"].Request().GetAsync();
Both of these returned the error:
Status Code: BadRequest
Microsoft.Graph.ServiceException: Code: BadRequest
Message: The $filter path segment must be in the form $filter(expression), where the expression resolves to a boolean.
This filter works fine when I use it in Postman calling the url directly. But I am trying to use their sdk and it is not working as expected.
What is wrong with this filter query?
$filter should be specified in Filter method. The article you followed does not reflect the current API.
var users = await _graphServiceClient.Users
.Request()
.Filter("startswith(mail,'test#email.com')")
.GetAsync();
Check documentation
I am trying to get sync jobs through c# code but getting unknown internal error. Here is code snippet :
var jobs = graphClient.ServicePrincipals["id"].Synchronization.Jobs.Request().GetAsync().Result;
getting Null jobs from this code.
When you want to list syncJobs, you need to add Directory.ReadWrite.All Delegated permission to app registered in Azure AD.
BTW, if you want to get syncJobs you could use the code as below:
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var synchronizationJob = await graphClient.ServicePrincipals["{id}"].Synchronization.Jobs["{jobId}"]
.Request()
.GetAsync();
I have created an auth provider using user-password auth provider but and trying to retrieve calendar events in bot code which is in c#, having bot framework 4
IPublicClientApplication publicClientApplication =
PublicClientApplicationBuilder
.Create("jhnjchdjvd")
.WithTenantId("sdfdf")
.Build();
var s = new SecureString();
s.AppendChar('<');
s.AppendChar('T');
s.AppendChar('N');
s.AppendChar('>');
s.AppendChar('7');
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
Microsoft.Graph.User me = await graphClient.Me.Request()
.WithUsernamePassword("kjkhv#indica.onmicrosoft.com",
s).GetAsync();
var events = await graphClient.Me.Events
.Request()
.Header("Prefer", "outlook.timezone=\"Pacific Standard Time\"")
.Select(e => new
{
e.Subject,
e.Body,
e.BodyPreview,
e.Organizer,
e.Attendees,
e.Start,
e.End,
e.Location
})
.GetAsync();
Above code throws exception which is
Microsoft.graph.serviceexception code generalexceptionmessage an error
occurred sending the request
How can I solve this error?
Make sure you use the latest NuGet Package and give a try. In order to get the latest one try the following:
"Install-Package Microsoft.Identity.Client -Pre".
This command
installs latest MSAL library.
Quickly i tested the sample. I know its not the BOT framework, but its MSAL & calling Graph, so the same logic applies and works on the principles. It worked for me.
Also i tested with BOT Framework and MSAL; here are the steps that i followed. You may want to check the same as well.
The same issue in my case was caused by revoking the Task function by .GetAwaiter().GetResult().
Solution: I replaced the .GetAwaiter().GetResult() with await approach.
I am working with an ASP.NET Core 2.0 application hosted on Azure and authenticates users through Microsoft using MSAL. I am getting the basic information through the authentication process like name, username and group claims. However, I want to access some additional information through MS Graph, like the users profile photo. Initial authentication and token acquisition runs smoothly, and sending a request to https://graph.microsoft.com/beta/me returns 200 OK. When trying to call https://graph.microsoft.com/beta/me/photo/$value, however, I get a 401 - Unauthorized in return.
I have seen several other posts on this issue, but most of them concludes that the developer have either forgotten to ask for the proper consents, gotten tokens from the wrong endpoints, or similar issues. All of which I have confirmed not to be the case.
I have confirmed that the proper scopes are included in the token using https://jwt.ms/. I also tried asking for greater scopes than necessary. Currently I am using the following scopes: openid profile User.ReadBasic.All User.Read.All User.ReadWrite Files.ReadWrite.All. According to the beta reference for get user the least required permission is User.Read and according to the reference for get photo the least required permission is also User.Read. Using the Graph Explorer I have also confirmed that I should have had access to the photo using the permissions that I do, although, I have not set any pictures on my profile so it gives me a 404 response.
I am at a loss as to why I cannot get access to the profile photo so any suggestions are much appreciated. If you need more information or details, please let me know. If relevant, I have a custom middleware that handles the post-authentication process of reading the user information which also makes the additional call to MS Graph for the photo.
Edit:
I also tried https://graph.microsoft.com/beta/users/{my-user-id}/photo/$value which yielded the same results - 404 in Graph Explorer and 401 through my code
Edit 2: Code
Here is the code that I am using. This first snippet is in a middleware that puts the claims from the authenticated user in a specific format. I have just been putting a break point on the return and inspected the response object.
public async Task GetUserPhotoAsync(string userid, HttpContext context)
{
HttpClient client = new HttpClient();
//client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var result = await new TokenHelper(_settings).GetAuthenticationAsync(userid, context, new string[] { "User.ReadBasic.All", "User.Read.All", "User.ReadWrite", "Files.ReadWrite.All" });
var url = "https://graph.microsoft.com/beta/me/photo/$value";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
return;
}
Here is the function that gets the token from the cache. MSALSessionCache is some code I have borrowed from here with some tweaks to fit .net core.
public async Task<AuthenticationResult> GetAuthenticationAsync(string signedInUserId, HttpContext context, string[] scopes)
{
TokenCache userTokenCache = new MSALSessionCache(signedInUserId, context).GetMsalCacheInstance();
ConfidentialClientApplication cca =
new ConfidentialClientApplication(_settings.ClientId, $"{_settings.Domain}/{_settings.AADInstance}/v2.0", "http://localhost:5000", new ClientCredential(_settings.ClientSecret), userTokenCache, null);
if (cca.Users.Count() > 0)
{
AuthenticationResult result = await cca.AcquireTokenSilentAsync(scopes, cca.Users.First());
return result;
}
else
{
throw new Exception();
}
}
Initial token acquisition
options.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async context =>
{
string signedInUserId = context.Principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserId, context.HttpContext).GetMsalCacheInstance();
ConfidentialClientApplication cca =
new ConfidentialClientApplication(aadOptions.ClientId, aadOptions.RedirectUri, new ClientCredential(aadOptions.ClientSecret), userTokenCache, null);
AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(context.ProtocolMessage.Code, new string[] { "User.ReadBasic.All", "User.Read.All", "User.ReadWrite", "Files.ReadWrite.All" });
context.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
};
Edit 3: Using the /v1.0 endpoint
As per Marc LaFleur's request I have tried the v1.0 endpoint with the same result. https://graph.microsoft.com/v1.0/me gives a 200 OK response code while https://graph.microsoft.com/v1.0/me/photo/$value returns 401 Unauthorized
I had the same problem with the Microsoft Graph giving a 401 Unauthorized exception when I was trying to query a user's photo or the photo's metadata on both the /V1.0 and /beta API endpoints. Like you, I verified I had the right tokens and was able to successfully access the user profile API.
In my case, I found it was because the photo for the user I was testing with hadn't been set. Once I assigned a photo I was able to successfully call both the photo value and photo metadata beta endpoints.
The v1.0 endpoints still gave me a 401 Unauthorized exception, but in my application I only use AzureAD, not Exchange. Based on #MarcLaFleur comments and the API documentation, this sounds like "expected" behaviour.
Why it returns a 401 Unauthorized instead of something like a 404 Not Found, or returning null values, I don't know.