Adding invited (guest) user to teams seems to not work properly - microsoft-graph-api

Hi (ref issue)
After setting up the tenant to allow invitation of user from another domain, we are able to invite external users (in set domain) to teams. This works fine when doing it manually, in the GUI.
However, when trying to add an invited user threw the windows graph API, something is not working properly.
Our procedure to invite a user to a team is as follows:
Note we are using application privileges
Invite the user to the tenant (with or without welcome mail)
https://learn.microsoft.com/en-us/graph/api/invitation-post?view=graph-rest-1.0
Add the invited user to the team
https://learn.microsoft.com/en-us/graph/api/group-post-members?view=graph-rest-1.0
Both these calls complete successfully and does not return any error messages. In all the admin GUI’s (AAD, Teams, Exchange) the user is invited and is added to the group.
But the user in question does not receive a welcome mail that he/she has been added to the team. And if the user (given we send a welcome mail in step 1) tries to access http://teams.microsoft.com the user gets notified that he/she does not have permissions and/or does not see the team.
Any tips?
API Permissions
EDIT:
After some investigation, by monitoring the network traffic. It's seems that the missing call, to get properly invited to the team is:
POST https://api.teams.skype.com/emea/beta/teams/($teamurl)/bulkUpdateRoledMembers?allowBotsInChannel=true
where you send in a list of userid (8:orgid:{userid}) and the groupid. (teamurl seems to be the channel id)
{"users":[{"mri":"8:orgid:00000000-5946-0000-87d2-b16b6fdf7a72","role":2}],"groupId":"00000000-2e8b-4d18-0000-394c6a4846d0"}
I have tried to call this from application & delegation, but get 'Unauthorized'. Also I could not find any API permission that granted access to 'api.teams.skype.com'.

I finally figured out how to get an access token to invoke bulkUpdateRoledMembers. It only works if I request an access token for it directly, so no Application Permissions and no On-Behalf-Of Flow.
private static async Task<string> GetAccessTokenForTeams(string tenantId)
{
var client = new PublicClientApplication(
clientId: "d3590ed6-52b3-4102-aeff-aad2292ab01c",
authority: $"https://login.microsoftonline.com/{tenantId}/",
userTokenCache: null);
try
{
var result = await client.AcquireTokenInteractive(new[] { "https://api.spaces.skype.com/user_impersonation" }, null).ExecuteAsync();
return result.AccessToken;
}
catch (Exception e)
{
Debug.WriteLine(e);
throw;
}
}
It turns out you also need a Skypetoken, which you can get very easily with the just acquired access token.
private static async Task<string> GetSkypeToken(string accessToken)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add(HttpRequestHeader.Authorization.ToString(), "Bearer " + accessToken);
using (var response = await client.PostAsync("https://api.teams.skype.com/beta/auth/skypetoken", null))
{
var contentString = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var skypeTokenResponse = JsonConvert.DeserializeObject<SkypeTokenResponse>(contentString);
return skypeTokenResponse.Tokens.SkypeToken;
}
else
{
throw new Exception(response.StatusCode.ToString() + ": " + contentString);
}
}
}
}
private class SkypeTokenResponse
{
public Token Tokens { get; set; }
public class Token
{
public string SkypeToken { get; set; }
public string ExpiresIn { get; set; }
}
}
Then you can finally invoke bulkUpdateRoledMembers by passing both tokens along.
private static async Task<object> bulkUpdateRoledMembers(string accessToken, string skypeToken, string teamUrl, string groupId, string userId)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add(HttpRequestHeader.Authorization.ToString(), "Bearer " + accessToken);
client.DefaultRequestHeaders.Add("X-Skypetoken", skypeToken);
var bodyString = JsonConvert.SerializeObject(new
{
users = new List<object>
{
new
{
mri = "8:orgid:" + userId,
role = 2
}
},
groupId = groupId
});
var body = new StringContent(bodyString, Encoding.UTF8, "application/json");
using (var response = await client.PutAsync($"https://teams.microsoft.com/api/mt/emea/beta/teams/{teamUrl}/bulkUpdateRoledMembers?allowBotsInChannel=true", body))
{
var contentString = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var jsonresult = JObject.Parse(contentString);
return jsonresult;
}
else
{
throw new Exception(response.StatusCode.ToString() + ": " + contentString);
}
}
}
}

Related

How to add JwT token in request header ASP.Net MVC Core 6

I have just started to use Asp.Net Core and I managed to create a mvc project. In This project I have created an API and it is secured with token based authorization.I have also used identity framework for user auhentication. Now I want to consume this API to perform CRUD operations with passing token but have no clear idea how to do that. After searching similar questions what I have tried is generate the token using user credentials (username, password) when user successfully logged in or registered and attach the generated token to header and as far as I know it will be passed through each subsequent request.
First I tried creating a method to call to generate the token after success login or registration. This includes in same controller which used for login and registration.
Token generate method
public string GenerateAuthToken(ApplicationUser applicationUser)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration.GetSection("JWT")["TokenSignInKey"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[] {
new Claim(type:JwtRegisteredClaimNames.Sub, applicationUser.Id),
new Claim(type:JwtRegisteredClaimNames.Email, applicationUser.Email),
new Claim(type:JwtRegisteredClaimNames.Iat,
value:DateTime.Now.ToUniversalTime().ToString())
}),
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var stringToken = tokenHandler.WriteToken(token);
return stringToken;
}
I call this after success user login and register,
public async Task<IActionResult> Register(RegisterViewModel registerViewModel)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = registerViewModel.Username,
Email = registerViewModel.Email};
var result = await _userManager.CreateAsync(user, registerViewModel.Password);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
var token = GenerateAuthToken(user);
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("bearer", token);
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError("", "User Registration Failed");
}
return View(registerViewModel);
}
When this executed, the token is successfully generated but does not attach the token. I do not know if I am doing any wrong here. But I found someone facing the same issue but has tried different way to achieve this. I think it is the correct way but not sure. Instead of generate the token on success login, have to generate it each api call. According to this solution I created another controller and action to generate the token.
public async Task<IActionResult> GetToken([FromBody] AuthViewModel authViewModel)
{
var user = _context.Users.FirstOrDefault(u => u.Email == authViewModel.Email);
if (user != null)
{
var signInResult = await _signInManager.CheckPasswordSignInAsync(user,
authViewModel.Password, false);
if (signInResult.Succeeded)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration.GetSection("JWT")
["TokenSignInKey"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[] {
new Claim(type:JwtRegisteredClaimNames.Sub,authViewModel.Email),
new Claim(type:JwtRegisteredClaimNames.Email,
authViewModel.Email),
new Claim(type:JwtRegisteredClaimNames.Iat,
value:DateTime.Now.ToUniversalTime().ToString())
}),
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(new
SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var stringToken = tokenHandler.WriteToken(token);
return Ok(new { Token = stringToken });
}
return BadRequest("Invalid User");
}}
AuthViewModel
public class AuthViewModel
{
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
}
I added authViewModel to accept logged user credentials since I don't want add them manually, Then I have created another controller to perform the CRUD same as the above mentioned link Please note that I followed the solution mentioned below that page.
private async Task<string> CreateToken()
{
var user = await _userManager.GetUserAsync(User);
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:7015/Auth");
request.Content = JsonContent.Create(new AuthViewModel{
Email = user.Email, Password = user.PasswordHash
});
var client = _clientFactory.CreateClient();
HttpResponseMessage response = await client.SendAsync(request);
var token = await response.Content.ReadAsStringAsync();
HttpContext.Session.SetString("JwToken", token);
return token;
}
request.Content I added to match my solution since token should be generated using user credentials. But I have no idea how to pass the logged in user's credentials with the request. This does not work. It is not possible to access the user password.
This is how I called the token generate action to perform CRUD. And I use JQuery Ajax to call the GetAllSales endpoint.
public async Task<IActionResult> GetAllSales()
{
string token = null;
var strToken = HttpContext.Session.GetString("JwToken");
if (string.IsNullOrWhiteSpace(strToken))
{
token = await CreateToken();
}
else
{
token = strToken;
}
List<Sale> sales = new List<Sale>();
var client = _clientFactory.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get,
"http://localhost:7015/api/Sales");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = await client.SendAsync(request,
HttpCompletionOption.ResponseHeadersRead);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var apiString = await response.Content.ReadAsStringAsync();
sales = JsonConvert.DeserializeObject<List<Sale>>(apiString);
}
Ok(sales);
}
This does not work. An exception throws
'System.InvalidOperationException: Unable to resolve service for type 'System.Net.Http.IHttpClientFactory' while attempting to activate '_7_ElevenRetail.Controllers.AccessApiController'.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)'
Please suggest me and show me how to achieve this correctly. I am expecting all of your help. Thank you.
System.InvalidOperationException: Unable to resolve service for type 'System.Net.Http.IHttpClientFactory' while attempting to activate '_7_ElevenRetail.Controllers.AccessApiController'
This issue means you inject IHttpClientFactory in AccessApiController without registering the service in Program.cs.
Register IHttpClientFactory by calling AddHttpClient in Program.cs:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddHttpClient();

Calling Graph API from Console Application using a User Credentials

I'm using following code to call Microsoft Graph API:
private static void GraphAPICallTest()
{
try
{
string authority = "https://login.microsoftonline.com/common/oauth2/token";
string resrouce = "https://graph.microsoft.com";
string clientId = "{clientid}";
string userName = "userid#test.com";
string password = "{userpassword}";
UserPasswordCredential userPasswordCredential = new UserPasswordCredential(userName, password);
AuthenticationContext authContext = new AuthenticationContext(authority);
var result = authContext.AcquireTokenAsync(resrouce, clientId, userPasswordCredential).Result;
var graphserviceClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
(requestMessage) =>
{
var access_token = authContext.AcquireTokenSilentAsync(resrouce, clientId).Result.AccessToken;
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", access_token);
return Task.FromResult(0);
}));
var a = graphserviceClient.Me.Request().GetAsync().Result;
Console.WriteLine("Success");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex}");
}
Console.ReadLine();
}
When I use any user from Azure AD this code works just fine. but if i use personnel account it errors out with following error.
ADSTS65001: The user or administrator has not consented to use the application with ID 'c15fd791-7a49-424b-a938-2a9464476277' named 'OneDriveWebAppTest'. Send an interactive authorization request for this user and resource.
Trace ID: 5f31be27-dfdd-410b-af41-e769b2573d00
Correlation ID: 9c8c9f60-c56d-4845-a223-37f5232c6210
Timestamp: 2017-12-11 13:58:39Z
Yes, error is asking about consenting with that personnel user. But even after doing the consent with that user, the error still persist.
Not sure if i'm missing anything.

ASP.net MVC: Accessing Google GoogleWebAuthorizationBroker returns access denied error

I'm trying to upload an video to my YouTube account with the following code in my ActionResult in my asp.net MVC project:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Upload([Bind(Include = " Title, Description")] HttpPostedFileBase uploadFile, Videos videos)
{
var credential = AuthYouTube();
YouTubeService youtubeService = new YouTubeService(new
YouTubeService.Initializer()
{
ApplicationName = "app-name",
HttpClientInitializer = credential
});
// Do Stuff with the video here.
}}
The AuthYouTube() looks like this (the same controller):
public UserCredential AuthYouTube()
{
string filePath = Server.MapPath("~/Content/YT/client_secret.json");
UserCredential credential;
try{
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows for full read/write access to the
// authenticated user's account.
new[] { YouTubeService.Scope.Youtube },
"username#domain.com",
CancellationToken.None,
new FileDataStore(Server.MapPath("~/Content/YT"),true)
).Result;
};
return credential;
}
catch(EvaluateException ex)
{
Console.WriteLine(ex.InnerException);
return null;
}
}
I have stored my client_secret.json that I downloaded from Google Developer Console inside the [project]/Content/YT. (Also tried inside the /App_Data folder.
When uploading the debugger is showing the folowwing message:
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ComponentModel.Win32Exception: Access is denied
Place where the error occures:
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
StackStrace:
[Win32Exception (0x80004005): Access is denied]
Microsoft.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +115
Microsoft.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccess(Task task) +78
Google.Apis.Auth.OAuth2.<AuthorizeAsync>d__1.MoveNext() in C:\Users\mdril\Documents\GitHub\google-api-dotnet-client\Src\GoogleApis.Auth.DotNet4\OAuth2\GoogleWebAuthorizationBroker.cs:59
[AggregateException: One or more errors occurred.]
System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification) +4472256
Project.Controllers.VideosController.AuthYouTube() in d:\dev\Development\project\project\Controllers\VideosController.cs:133
project.Controllers.VideosController.Upload(HttpPostedFileBase uploadFile, Videos videos) in d:\dev\project\project\Controllers\VideosController.cs:71
What is the reason of this?
- Google API?
- folder / IIS rights?
Update 01-02-2016
Could it be some access error on the API side?
If not, could somebody please provide me the steps to grand the right IIS rights, still get the error after giving folder permissions.
Running the following code DOES create the folder as intended inside my App_Data, but also returns the same 'Access denied' error. The folder is empty.
var path = HttpContext.Current.Server.MapPath("~/App_Data/Drive.Api.Auth.Store");
// here is where we Request the user to give us access, or use the Refresh Token that was previously stored in %AppData%
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
, scopes
, userName
, CancellationToken.None
, new FileDataStore(path,true)).Result;
Could somebody please explain how to get this working?
After ready the documentation again I found a way to get access to the API and upload my videos to YouTube. I hope I can clarify the way i did this.
How i did this:
https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web-applications-aspnet-mvc
Create an callback controller:
using Google.Apis.Sample.MVC4;
namespace Google.Apis.Sample.MVC4.Controllers
{
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
{
protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData
{
get { return new AppFlowMetadata(); }
}
}
}
Create class and fill-in the credentials:
using System;
using System.Web.Mvc;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.YouTube.v3;
using Google.Apis.Util.Store;
namespace Google.Apis.Sample.MVC4
{
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "PUT_CLIENT_ID_HERE",
ClientSecret = "PUT_CLIENT_SECRET_HERE"
},
Scopes = new[] { YouTubeService.Scope.YoutubeUpload },
DataStore = new FileDataStore(HttpContext.Current.Server.MapPath("~/App_Data/clientsecret.json")),
});
public override string AuthCallback
{
get { return #"/AuthCallback/Upload"; }
}
public override string GetUserId(Controller controller)
{
// In this sample we use the session to store the user identifiers.
// That's not the best practice, because you should have a logic to identify
// a user. You might want to use "OpenID Connect".
// You can read more about the protocol in the following link:
// https://developers.google.com/accounts/docs/OAuth2Login.
var user = controller.Session["user"];
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}
}
In my ActionResult I set the YoutubeService. the creating of my video take place inside my Upload POST
Your own controller (mine is for the /upload action):
public async Task<ActionResult> Upload(CancellationToken cancellationToken)
{
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
if (result.Credential != null)
{
var youtubeService = new YouTubeService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "name",
});
return View();
}
else
{
return new RedirectResult(result.RedirectUri);
}
}
For uploading logic see: https://developers.google.com/youtube/v3/code_samples/dotnet#upload_a_video
Set redirect URL in Google Developers console
In the Google Developers Console set the Authorized redirect URIs value to something like (my controller is called videos): http://www.domainname.com/Videos/Upload
**Using a single oAuth account **
Insted of saving the client id (GUID, see GetUserId inside AppFlowMetadata file) inside my session I now use one single id so I could use the same token/responsive for all the users.

SteamAuth var from startup.auth to the view ASP.NET

I want to get the profile information from steam. so first I've fixed that I can login through steam, I used this tutorial: http://www.oauthforaspnet.com/providers/steam/
But now I want to get the steam profile id from the user that logged in so I can use the JSON from the steam API to get the information from the user.
https://steamcommunity.com/profiles/(this id)
I hope someone can help me, I've searched for hours now, and don't have any result.
var options = new SteamAuthenticationOptions {
ApplicationKey = "Your API Key",
Provider = new OpenIDAuthenticationProvider // Steam is based on OpenID
{
OnAuthenticated = async context =>
{
// Retrieve the user's identity with info like username and steam id in Claims property
var identity = context.Identity;
}
}}; app.UseSteamAuthentication(options);
A while ago we discovered the answer:
1.) insert your key from the tutorial here:
var options = new SteamAuthenticationOptions
{
ApplicationKey = "Your API Key",
Provider = new OpenIDAuthenticationProvider // Steam is based on OpenID
{
OnAuthenticated = async context =>
{
// Retrieve the user's identity with info like username and steam id in Claims property
var identity = context.Identity;
}
}
};
app.UseSteamAuthentication(options);
2.) We discovered that steam is saving a users steam id in the database table called: 'AspNetUserLogins', the providerkey inside that table is an url made out of more pieces. For example:
http://steamcommunity.com/openid/id/here-users-steamid
We only need the users steamid, so we going to split that in step 3.
3.) Make a controller, for example: SteamController. Here we going to add a public string:
public string GetSteamID()
{
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new Steam.Models.ApplicationDbContext()));
var CurrentUser = manager.FindById(User.Identity.GetUserId());
if (User.Identity.Name != "")
{
string url = CurrentUser.Logins.First().ProviderKey;
ViewBag.steamid = url.Split('/')[5]; //here we going to split the providerkey so we get the right part
}
else
{
ViewBag.steamid = "";
}
return ViewBag.steamid;
}
) now we can add some stuff, lets say we going to add profile information. Go to your SteamController and add:
[HttpGet]
public ContentResult GetProfile()
{
string url = string.Format("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=addyourkeyhere&steamids={0}", this.GetSteamID());
string result = null;
using (var client = new WebClient())
{
result = client.DownloadString(url);
}
return Content(result, "application/json");
}
notice that you have to add your steam key from step 1 in the url.
) make a script called: profile.js. Here we going to add our profile information.
function profilepic() {
$.ajax({
url: 'http://localhost:3365/steam/GetProfile',
dataType: 'json',
success: function (data) {
$.each(data.response.players, function (key, value) {
if ($('.profile')) {
$('.profile').append("<img src='" + value.avatar + "'> <span>" + value.personaname + "</span>")
}
if ($('.profile1')) {
$('.profile1').append("<img src='" + value.avatarfull + "'>")
}
if ($('.username')) {
$('.username').append(value.personaname)
}
console.log(value)
});
}, error: function (httpReq, status, exception) {
console.log(status + " " + exception);
}
});
}
6.) Now we have to do the final step, create a view with the classes, for example:
<div class="userprofile">
<span class="profile1"></span>
<div class="userdescription">
<h2 class="username"></h2>
</div>
</div>
) I hope this will help some people, for more questions, feel free to ask!

Azure ActiveDirectory Graph API GraphClient not returning AD Groups

I want to retrieve a User's Group information from Azure AD.
Using the following Graph API packages to achieve this
Microsoft.Azure.ActiveDirectory.GraphClient
Microsoft.IdentityModel.Clients.ActiveDirectory 2.13.112191810
I am able to successfully retrieve Users information from the Azure Graph API.
But when I run this method to retrieve a User's groups, Fiddler shows a successful HTTP 200 response with JSON fragment containing group information however the method itself does not return with the IEnumerable.
IEnumerable<string> groups = user.GetMemberGroupsAsync(false).Result.ToList();
The code doesn't seem to return from this async request.
The resulting experience is blank page while the authentication pipeline gets stuck.
Full code
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (!incomingPrincipal.Identity.IsAuthenticated == true &&
_authorizationService.IdentityRegistered(incomingPrincipal.Identity.Name))
{
return base.Authenticate(resourceName, incomingPrincipal);
}
_authorizationService.AddClaimsToIdentity(((ClaimsIdentity) incomingPrincipal.Identity));
Claim tenantClaim = incomingPrincipal.FindFirst(TenantIdClaim);
if (tenantClaim == null)
{
throw new NotSupportedException("Tenant claim not available, role authentication is not supported");
}
string tenantId = tenantClaim.Value;
string authority = String.Format(CultureInfo.InvariantCulture, _aadInstance, _tenant);
Uri servicePointUri = new Uri("https://graph.windows.net");
ClientCredential clientCredential = new ClientCredential(_clientId, _password);
AuthenticationContext authContext = new AuthenticationContext(authority, true);
AuthenticationResult result = authContext.AcquireToken(servicePointUri.ToString(), clientCredential);
Token = result.AccessToken;
ActiveDirectoryClient activeDirectoryClient =
new ActiveDirectoryClient(new Uri(servicePointUri, tenantId),
async () => await AcquireTokenAsync());
IUser user = activeDirectoryClient
.Users
.Where(x => x.UserPrincipalName.Equals(incomingPrincipal.Identity.Name))
.ExecuteAsync()
.Result
.CurrentPage
.ToList()
.FirstOrDefault();
if (user == null)
{
throw new NotSupportedException("Unknown User.");
}
IEnumerable<string> groups = user.GetMemberGroupsAsync(false).Result.ToList();
return incomingPrincipal;
}
I have the same problem. My code is working after changing it according to documentation
https://github.com/AzureADSamples/ConsoleApp-GraphAPI-DotNet
IUserFetcher retrievedUserFetcher = (User) user;
IPagedCollection<IDirectoryObject> pagedCollection = retrievedUserFetcher.MemberOf.ExecuteAsync().Result;
do {
List<IDirectoryObject> directoryObjects = pagedCollection.CurrentPage.ToList();
foreach (IDirectoryObject directoryObject in directoryObjects) {
if (directoryObject is Group) {
Group group = directoryObject as Group;
((ClaimsIdentity)incomingPrincipal.Identity).AddClaim(
new Claim(ClaimTypes.Role, group.DisplayName, ClaimValueTypes.String, "GRAPH"));
}
}
pagedCollection = pagedCollection.GetNextPageAsync().Result;
} while (pagedCollection != null && pagedCollection.MorePagesAvailable);
IEnumerable, string groups = user.GetMemberGroupsAsync(false).Result.ToList() doesn't work since the result is not of type IEnumerable, string.
IEnumerable<string> groups = await user.GetMemberGroupsAsync(false);
Above code would return the correct type.

Resources