Authentication refused to display Inside iframe with MVC app - asp.net-mvc

I have created an MVC application and in the Home page, I have a Power BI Dashboard Report, so I have configured the Power BI and Azure AD configuration in Index action, once Index action is called, it will verify the Authentication and Redirect to Redirect Action Method. In the method, Authentication has been verified and calls the Power BI action method and shows the report.
The report is working fine in the page, but when set it to Iframe, it is not working and shows the below error.
Home Page Index Action:
public ActionResult Index()
{
var #params = new NameValueCollection
{
//Azure AD will return an authorization code.
//See the Redirect class to see how "code" is used to AcquireTokenByAuthorizationCode
{"response_type", "code"},
//Client ID is used by the application to identify themselves to the users that they are requesting permissions from.
//You get the client id when you register your Azure app.
{"resource", "https://analysis.windows.net/powerbi/api"},
{"redirect_uri", "xxxx/home/Redirect."}
};
//Create sign-in query string
var queryString = HttpUtility.ParseQueryString(string.Empty);
queryString.Add(#params);
string authorityUri = "https://login.windows.net/common/oauth2/authorize/";
var authUri = String.Format("{0}?{1}", authorityUri, queryString);
ViewBag.authUri = authUri;
return View();
}
Redirect Action Method:
public async Task<ActionResult> Redirect()
{
string code = Request.Params["code"];
if (code != null)
{
AuthenticationContext AC = new AuthenticationContext("https://login.windows.net/common/oauth2/authorize", TC);
ClientCredential cc = new ClientCredential("xxxxx", "xxxxxxx");
AuthenticationResult AR = await AC.AcquireTokenByAuthorizationCodeAsync(code, new Uri("http://localhost:43333/home/redirect"), cc);
//Set Session "authResult" index string to the AuthenticationResult
Session["authResult"] = AR;
} else {
//Remove Session "authResult"
Session["authResult"] = null;
}
return RedirectToAction("POWERBI", "Home");
}
Power BI action:
public async Task<ActionResult> POWERBI()
{
AuthenticationResult authResult;
authResult = (AuthenticationResult)Session["authResult"];
var token = authResult.AccessToken;
ViewBag.Token = token;
var tokenCredentials = new TokenCredentials(token, "Bearer");
// Create a Power BI Client object. It will be used to call Power BI APIs.
using (var client = new PowerBIClient(new Uri(ApiUrl), tokenCredentials))
{
// Get a list of dashboards.
var dashboards = await client.Dashboards.GetDashboardsInGroupAsync(GroupId);
// Get the first report in the group.
var dashboard = dashboards.Value.FirstOrDefault();
//var dashboard = dashboards.Value.Where(w => w.Id == "DashboardId");
if (dashboard == null)
{
return View(new EmbedConfig()
{
ErrorMessage = ""
});
}
// Generate Embed Token.
var generateTokenRequestParameters = new GenerateTokenRequest(accessLevel: "view");
var tokenResponse = await client.Dashboards.GenerateTokenInGroupAsync(GroupId, dashboard.Id, generateTokenRequestParameters);
if (tokenResponse == null)
{
return View(new EmbedConfig()
{
ErrorMessage = "."
});
}
// Generate Embed Configuration.
var embedConfig = new EmbedConfig()
{
EmbedToken = tokenResponse,
EmbedUrl = dashboard.EmbedUrl,
Id = dashboard.Id
};
return View(embedConfig);
}
}
In home page view inside iframe:
<iframe src="#Url.Action("Index", "Home")" class="col-lg-12 col-md-12 col-sm-12" height="450"> </iframe>
NOTE :
- Feature is working fine without Iframe.
- problem while show the report in Iframe.
Error:
Refused to display url in a Iframe because it sets X-frame-options-to deny

The error message means the <iframe src> you are trying to pull into your site doesn't allow it to be hosted within a iframe. It is sending down the response header:
X-Frame-Options: DENY
And the browser is blocking the framing. The host page is doing this to prevent cross-frame scripting attacks.

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();

Adding invited (guest) user to teams seems to not work properly

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);
}
}
}
}

Using ASP.NET identity to do external oauth logins, how can add parameters to the facebook authorization endpoint? I want to pass "display=popup"

I'm using ASP.NET MVC 6 (.net core). With it, i'm using the built in external login logic in order to authenticate with facebook.
I've made a modification to it so that instead of authenticating within the same window, i'm launching a popup and authenticating there. Once successful, the popup closes itself and tells my main window to redirect. This all works.
However, I want to use the "smaller/mini" version of the facebook login page. This can be seen here:
https://www.facebook.com/login.php?display=popup
"display=popup" is what is controlling it.
I don't see how i can inject this kvp in my C# code. Where can i do it?
app.UseFacebookAuthentication(new FacebookOptions
{
// was hoping for something here... tried to stick it into the authorizationurl but then i end up with 2 question marks and it fails
AppId = "blah",
AppSecret = "blah"
});
[AllowAnonymous]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
// Don't see anything here...
return Challenge(properties, provider);
}
You can use OnRedirectToAuthorizationEndpoint event:
var facebookOptions = new FacebookOptions
{
AppId = "",
AppSecret = "",
Events = new OAuthEvents()
{
OnRedirectToAuthorizationEndpoint = ctx =>
{
ctx.HttpContext.Response.Redirect(ctx.RedirectUri + "&display=popup&pip");
return Task.FromResult(0);
}
}
};
app.UseFacebookAuthentication(facebookOptions);

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.

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