I'm trying to retrieve a (paged) list of all users in the directory, with the manager property expanded. When I run the following HTTP query it works as I want:
https://graph.windows.net/DOMAIN/users/?$expand=manager&api-version=2013-11-08
However I don't seem to grasp how to make the same query with the Azure AD Graph client. This is what I'm trying:
var userResult = _activeDirectoryClient.Users.Expand(x => x.Manager).ExecuteAsync().Result;
I adapted the following from the example at https://github.com/AzureADSamples/ConsoleApp-GraphAPI-DotNet/blob/master/GraphConsoleAppV3/Program.cs, please give it a shot:
List<IUser> usersList = null;
IPagedCollection<IUser> searchResults = null;
try
{
IUserCollection userCollection = activeDirectoryClient.Users;
userResult = userCollection.ExecuteAsync().Result;
usersList = searchResults.CurrentPage.ToList();
}
catch (Exception e)
{
Console.WriteLine("\nError getting User {0} {1}", e.Message,
e.InnerException != null ? e.InnerException.Message : "");
}
if (usersList != null && usersList.Count > 0)
{
do
{
usersList = searchResults.CurrentPage.ToList();
foreach (IUser user in usersList)
{
Console.WriteLine("User DisplayName: {0} UPN: {1} Manager: {2}",
user.DisplayName, user.UserPrincipalName, user.Manager);
}
searchResults = searchResults.GetNextPageAsync().Result;
} while (searchResults != null);
}
else
{
Console.WriteLine("No users found");
}
The GraphClient does not implement all of the features in the Graph API just yet.
New features are being added to the GraphClient over time and will be announced on the AAD team blog:
http://blogs.msdn.com/b/aadgraphteam/
And updates will be available in the nuget package ( Microsoft Azure Active Directory Graph Client Library ).
You can make what you need by making Http calls to the url you have in the question and get the response back as Json like this:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://graph.windows.net/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("DOMAIN/users/?$expand=manager&api-version=2013-11-08");
if (response.IsSuccessStatusCode)
{
// TODO: Deserialize the response here...
}
}
Related
I have a method that deletes multiple events. Currently the code is as following:
public async Task DeleteEvents(IEnumerable<string> eventExternalIds)
{
foreach(var eventExternalId in eventExternalIds)
{
await DeleteEvent(eventExternalId);
}
}
public async Task DeleteEvent(string eventExternalId)
{
await GraphClient
.Users[Username]
.Calendars[CalendarName]
.Events[eventExternalId]
.Request()
.DeleteAsync();
}
I would imagine it won't perform well with any significant number of id's to delete. Is there a way to delete them all in a batch(es) instead of each individually?
msgraph-sdk-dotnet v1.15.0 or above
For msgraph-sdk-dotnet version 1.15.0 or above the support for Batch request has been introduced via BatchRequestContent class
Example
//1. construct a Batch request
var batchRequestContent = new BatchRequestContent();
var step = 1;
foreach (var eventId in eventIds)
{
var requestUrl = graphClient
.Me
.Events[eventId]
.Request().RequestUrl;
var request = new HttpRequestMessage(HttpMethod.Delete, requestUrl);
var requestStep = new BatchRequestStep(step.ToString(), request, null);
batchRequestContent.AddBatchRequestStep(requestStep);
step++;
}
//2. Submit request
var batchRequest = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/v1.0/$batch");
batchRequest.Content = batchRequestContent;
await graphClient.AuthenticationProvider.AuthenticateRequestAsync(batchRequest);
var httpClient = new HttpClient();
var batchResponse = await httpClient.SendAsync(batchRequest);
//3. Process response
var batchResponseContent = new BatchResponseContent(batchResponse);
var responses = await batchResponseContent.GetResponsesAsync();
foreach (var response in responses)
{
if (response.Value.IsSuccessStatusCode)
{
//...
}
}
Issues
while targeting NetCore 2.1 or above or .NET Framework
NullReferenceException exception might occur, to address this issue
you could switch to 1.16.0-preview.1 (details)
Limitations
Note: A batch cannot hold more that 20 requests
msgraph-sdk-dotnet v1.14.0 or older
For previous versions, the following example demonstrates how to implement a support for Batch request:
var batchRequest = new BatchRequest();
foreach (var eventId in eventIds)
{
var request = graphClient.Me.Events[eventId].Request();
batchRequest.AddQuery(request,HttpMethod.Delete);
}
var responses = await graphClient.SendBatchAsync(batchRequest);
where BatchRequest is a custom class which adds support for JSON Batching
I am attempting to query Azure Active Directory User information using Microsoft Graph. I can authenticate fine but when I attempt to query user information client.Users my application hangs indefinitely: no timeout, no error, just hangs. I found this post however the suggestions there did not help me.
public bool GetUserByUniqueID(string uid, out GraphUser user)
{
bool ret = false;
user = new GraphUser();
if (Authenticate(out AuthToken token))
{
GraphServiceClient client = GetGraphServiceClient(token);
// The below code hangs indefinitely
User user = client.Users[uid].Request().Select(GraphProperties).GetAsync().GetAwaiter().GetResult();
if (user != null)
{
MapGraphUser(ret, user);
ret = true;
}
}
return ret;
}
private bool Authenticate(out AuthToken token)
{
bool ret = false;
token = new AuthToken();
string url = $"https://login.microsoftonline.com/{_tenant}/oauth2/v2.0/token";
RestClient client = new RestClient(url);
RestRequest request = new RestRequest(Method.POST);
request.Parameters.Add(new Parameter("grant_type", _grantType, ParameterType.GetOrPost));
request.Parameters.Add(new Parameter("scope", _scope, ParameterType.GetOrPost));
request.Parameters.Add(new Parameter("client_secret", _clientSecret, ParameterType.GetOrPost));
request.Parameters.Add(new Parameter("client_id", _clientId, ParameterType.GetOrPost));
IRestResponse response = client.Execute<AuthToken>(request);
if (response.StatusCode == HttpStatusCode.OK)
{
token = JsonConvert.DeserializeObject<AuthToken>(response.Content);
ret = true;
}
return ret;
}
Update 5/2/2019
Reverting Microsoft.Graph and Microsoft.Graph.Core to version 1.12 allows me to call .GetAwaiter().GetResult() within a synchronous context.
Update 11/18/2020
I have refactored my code to use async/await pattern with the latest version of Microsoft.Graph and Microsoft.Graph.Core.
public async Task<GraphUser> GetUserByUniqueID(string uid)
{
GraphUser ret = new GraphUser();
if (Authenticate(out AuthToken token))
{
GraphServiceClient client = GetGraphServiceClient(token);
User user = await client.Users[uid].Request().Select(GraphProperties).GetAsync();
if (user != null)
{
MapGraphUser(ret, user);
ret.Found = true;
}
}
return ret;
}
I was having the same issue. I found on another article somewhere that it had something to do with two task waiting to finish at once. i cant find that article now.
For me .GetAwaiter().GetResult(); was working within a scheduled job but not as a manual button press task.
as a result of playing around with it. What worked for me was replacing .GetAwaiter().GetResult() with await. (i'm not sure why this fixed it but it did)
From:
var results = graphServiceClient.Users[uid].Request().GetAsync().GetAwaiter().GetResult();
To:
var results = await graphServiceClient.Users[uid].Request().GetAsync();
Hope this helps someone in the future
I'm having the same issue with NPM package of the Graph API. Reverted to plain old request-promise. Now it's not stuck but does not always find the members of a group. Using beta version of the API works fine
I had the same issue when trying to get a list of sites, but I was using Microsoft.Graph V 4.47.0 and Microsoft.Graph.Core V 2.0.14, from within a MVC Web project. I was also using await.
var drives = await graphClient.Sites["root"].Lists
.Request()
.GetAsync();
The above just hangs. Changing to:
var drives = graphClient.Sites["root"].Lists
.Request()
.GetAsync()
.GetAwaiter()
.GetResult();
works as expected.
Full Code:
public async Task GetDrives(GraphServiceClient graphClient)
{
AuthenticationConfig config = AuthenticationConfig.ReadFromJsonFile("appsettings.json");
O365Drives = new List<MyDriveInfo>();
var drives = graphClient.Sites["root"].Lists
.Request()
.GetAsync()
.GetAwaiter()
.GetResult();
foreach (var item in drives)
{
O365Drives.Add(new MyDriveInfo
{
Id = item.Id,
Name = item.Name,
WebUrl = item.WebUrl,
CreatedOn = item.CreatedDateTime,
ModifiedOn = item.LastModifiedDateTime
});
}
}
The above is called by firing an Ajax POST request when clicking on a button.
In another project, a console app, using Microsoft.Graph V 4.34.0 and Microsoft.Graph.Core V 2.0.9
var drives = await graphClient.Sites["root"].Lists
.Request()
.GetAsync();
Works as expected.
Full Code:
private static async Task GetDrives(GraphServiceClient graphClient)
{
AuthenticationConfig config = AuthenticationConfig.ReadFromJsonFile("appsettings.json");
myFileInfo.O365Drives = new List<MyDriveInfo>();
var drives = await graphClient.Sites[$"{config.SiteID}"].Lists
.Request()
.GetAsync();
foreach(var item in drives)
{
myFileInfo.O365Drives.Add(new MyDriveInfo
{
Id = item.Id,
Name = item.Name,
WebUrl = item.WebUrl,
CreatedOn = item.CreatedDateTime,
ModifiedOn = item.LastModifiedDateTime
});
}
}
The above is called by either running the console app manually or from a scheduled task.
I just thought I'd post my findings for the newer versions of Microsoft.Graph for anyone else having similar issues.
Blockquoteafter access token when I called graph API that returns Authorization_RequestDenied request for the access token
using (var webClient = new WebClient())
{
var requestParameters = new NameValueCollection();
requestParameters.Add("resource", resource);
requestParameters.Add("client_id", clientID);
requestParameters.Add("grant_type", "client_credentials");
requestParameters.Add("client_secret", secret);
var url = $"https://login.microsoftonline.com/{tenant}/oauth2/token";
var responsebytes = await webClient.UploadValuesTaskAsync(url,"POST",requestParameters);
var responsebody =Encoding.UTF8.GetString(responsebytes);
var obj = JsonConvert.DeserializeObject<JObject>(responsebody);
var token = obj["access_token"].Value<string>();
access_token = token;
}
after when i request form get the user list from Azure AD by this way
public async Task<List<listItems>> GetData1( string token)
{
HttpClient http = new HttpClient();
string query = "https://graph.microsoft.com/v1.0/users";
HttpRequestMessage httpClient = new HttpRequestMessage(HttpMethod.Get, query);
httpClient.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var res = await http.SendAsync(httpClient);
var res1= await res.Content.ReadAsStringAsync();
List<listItems> lstUsers = new List<listItems>();
JObject results = JObject.Parse(res1); listItems itm;
foreach (var Jelem in results["value"])
{
string id = (string)Jelem["id"];
string displayName = (string)Jelem["displayName"];
itm = new listItems(); itm.id = id;
itm.displayname = displayName; lstUsers.Add(itm);
}
return lstUsers;
}
than i got "error": { "code": "Authorization_RequestDenied", "message": "Insufficient privileges to complete the operation.", "innerError": { "request-id": "1ba8a3e3-7e27-4bad-affd-6929b9af3a9f", "date": "2019-03-26T10:56:26" } the above error
please help me to solve this error
CAUSE
This problem occurs because the application does not have the required permission to access the user information. So you need to assign necessary privileged for this request.
SOLUTION
To access https://graph.microsoft.com/v1.0/users API One of the following permissions is required.
Permission type (from least to most privileged)
Delegated (work or school account) User.Read, User.ReadWrite,
User.ReadBasic.All,
User.Read.All, User.ReadWrite.All, Directory.Read.All,
Directory.ReadWrite.All,
Directory.AccessAsUser.All
Delegated (personal Microsoft account) User.Read, User.ReadWrite
Application User.Read.All, User.ReadWrite.All, Directory.Read.All,
Directory.ReadWrite.All
See the screen shot below:
AZURE PORTAL WAY OUT
To assign permission on azure portal see the screen shot below:
ASP.NET WEB FORM EXAMPLE:
1. Add New Aspx page To project
Take a new web form, here I have taken as Token.aspx and set its property like below
<%# Page Language="C#" AutoEventWireup="true" Async="true"
CodeBehind="Token.aspx.cs" Inherits="WebFormTest.Token" %>
2. Add New Reference from Nuget
In your project reference add a new service reference from nuget package manager console Like below:
3. Token.aspx.cs
Paste following code outside the scope of Page_Load method You might need to add following reference on your namespace once you encounter missing reference error.
using System.Net.Http;
using System.Net.Http.Headers;
class AccessToken
{
public string access_token { get; set; }
}
// Resource Owner Password Credentials Format
private async Task<string> GetTokenByROPCFormat()
{
string tokenUrl = $"https://login.microsoftonline.com/YourTenantId/oauth2/token";
var req = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
req.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "password",
["client_id"] = "ApplicationID",
["client_secret"] = "ApplicationSecret",
["resource"] = "https://graph.microsoft.com",
["username"] = "userEmailwithAccessPrivilege",
["password"] = "YourPassword"
});
dynamic json;
dynamic results;
HttpClient client = new HttpClient();
var res = await client.SendAsync(req);
json = await res.Content.ReadAsStringAsync();
//Token Output
results = JsonConvert.DeserializeObject<AccessToken>(json);
Console.WriteLine(results.access_token);
//New Block For Accessing Data from Microsoft Graph API
HttpClient newClient = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", results.access_token);
HttpResponseMessage response = await newClient.SendAsync(request);
string output = await response.Content.ReadAsStringAsync();
Console.WriteLine("Responsed data Is-\n\n" + output + "");
return output;
}
4. Call GetTokenByROPCFormat() Method inside Page_Load
Now call GetTokenByROPCFormat inside the Page_Load like below
RegisterAsyncTask(new PageAsyncTask(GetTokenByROPCFormat));
5. Token Output
If you set debugger on results variable you would get your token like below
6. Access Microsoft Graph API
Now move to following line and set your debugger like below
string output = await response.Content.ReadAsStringAsync();
You would see following output
Hope it would solve your problem. Thank you.
Since twitter is depreciating API version 1 soon I've decided to convert an older Application to allow it to work with the new 1.1 API. From what I know about 1.1 I know you have to authenticate before making a call and use JSON rather than RSS for serializing the data. The application is WPF coded using xmal and c#
I am able to successfully authenticate using the LINQ to Twitter Library but I am lost when it comes to using JSON. Here is my code that I used for API v1
else if (auth.IsAuthorized && i == 2)
{
SyndicationClient client = new SyndicationClient();
SyndicationFeed feed = await client.RetrieveFeedAsync(new Uri("https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=ScreenName"));
{
_model.Tweets.Clear();
foreach (var item in feed.Items)
{
_model.Tweets.Add(new Tweet
{
Name = "#ExampleHandle",
Message = item.Title.Text,
Image = new BitmapImage(new Uri("ms-appx:Assets/test_image", UriKind.RelativeOrAbsolute)),
});
}
}
}
}
And here is the code for the tweet class
public class Tweet
{
public String Name { get; set; }
public String Message { get; set; }
public ImageSource Image { get; set; }
}
I was wondering if someone could point me in the right direction for writing the JSON equivalent of this. Thanks in advance!
For those you read this question later on I was able to solve this problem. Below are my answers depending on your situation.
If you simply want to use Json instead of RSS you can do it like this:
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(new Uri("https://api.twitter.com/1/statuses/user_timeline.json?screen_name=ScreenName"));
string ApiResponse = await response.Content.ReadAsStringAsync();
List<Tweet> tweets = await JsonConvert.DeserializeObjectAsync<List<Tweet>>(ApiResponse);
_model.Tweets.Clear();
foreach (var item in tweets)
{
_model.Tweets.Add(new Tweet
{
Name = "#UserName",
Message = item.Text,
Image = new BitmapImage(new Uri("ms-appx:Assets/sampleLocalImage", UriKind.RelativeOrAbsolute)),
});
However because of API 1.1 you must be authenticated before EACH call to the API for this is used Linq to Twitter. Here is the code for Authorization:
var auth = new SingleUserAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = TwitterSettings.ConsumerKey,
ConsumerSecret = TwitterSettings.ConsumerKeySecret,
OAuthToken = TwitterSettings.AccessToken,
AccessToken = TwitterSettings.AccessTokenSecret,
}
};
auth.Authorize();
And the Code to Perform a Search(This is the code you want to use if using Twitter API 1.1):
var twitterCtx = new TwitterContext(auth);
var statusTweets =
from tweet in twitterCtx.Status
where tweet.Type == StatusType.User
&& tweet.ScreenName == "ScreenName"
select tweet;
_model.Tweets.Clear();
foreach (var item in statusTweets)
{
_model.Tweets.Add(new Tweet
{
Name = item.User.Name,
Message = item.Text,
Image = new BitmapImage(new Uri(item.User.ProfileImageUrl)),
});
I'm not familiar with the Twitter API, but I would assume some combination of HttpClient (if you're on .NET 4.0, you can get it here) and Newtonsoft.Json would be appropriate.
Newtonsoft.Json is not authored by Microsoft, but it is the package that everyone uses (including Microsoft's default web templates). The old Microsoft JSON serialization stuff is pretty much dead at this point.
I have a Windows 8 Store Application and I want to add Azure Authentication to it. I have followed the example in the MSDN page. However, the following line is giving me issues:
MobileServiceUser loginResult = await App.MobileService.LoginAsync(result.Session.AuthenticationToken);
The error is: App does not contain a definition for MobileService. When does an instance of MobileService get added to the App class?
I have added references to the Microsoft.Live and Azure Mobile Services libraries. Here is the entire Authenticate function:
private async System.Threading.Tasks.Task Authenticate()
{
LiveAuthClient liveIdClient = new LiveAuthClient("<< INSERT REDIRECT DOMAIN HERE >>");
while (session == null)
{
// Force a logout to make it easier to test with multiple Microsoft Accounts
if (liveIdClient.CanLogout)
liveIdClient.Logout();
LiveLoginResult result = await liveIdClient.LoginAsync(new[] { "wl.basic" });
if (result.Status == LiveConnectSessionStatus.Connected)
{
session = result.Session;
LiveConnectClient client = new LiveConnectClient(result.Session);
LiveOperationResult meResult = await client.GetAsync("me");
MobileServiceUser loginResult = await App.MobileService.LoginAsync(result.Session.AuthenticationToken);
string title = string.Format("Welcome {0}!", meResult.Result["first_name"]);
var message = string.Format("You are now logged in - {0}", loginResult.UserId);
var dialog = new MessageDialog(message, title);
dialog.Commands.Add(new UICommand("OK"));
await dialog.ShowAsync();
}
else
{
session = null;
var dialog = new MessageDialog("You must log in.", "Login Required");
dialog.Commands.Add(new UICommand("OK"));
await dialog.ShowAsync();
}
}
}
You have to add the class yourself.
On the Azure Mobile Services "Getting Started Page", select "Windows Store", and then "CONNECT AN EXISTING WINDOWS STORE APP" (don't mean to shout, it's literally in all caps on the page like that!).
It will tell you to do the following:
Add "using Microsoft.WindowsAzure.MobileServices;", then copy and
paste the following code into your App.xaml.cs file:
public static MobileServiceClient MobileService = new MobileServiceClient(
"https://[your website].azure-mobile.net/",
"[your key]"
);