I'm trying to retrieve all users for a given AD domain. Whilst I have managed to retrieve the user list successfully, the next step is to identify which groups the user is a member of. This is where it gets hard.
Step 1)
var clientSecretCredential = new ClientSecretCredential(configuration.TenantId, configuration.ClientId, ClientSecret);
GraphServiceClient graphClient = new GraphServiceClient(clientSecretCredential);
return await Task.FromResult(graphClient);
This gets me a successful connection to the GraphClient
var users = await graphClient.Users.Request().GetAsync();
foreach (var user in users)
{
Console.WriteLine(user.DisplayName);
}
displays the list of users (using their DisplayName)
Step 2)
var groups = await graphClient.Users[user.Id].TransitiveMemberOf.Request().GetAsync();
This gets me the user's groups. Finally I would like to display the actual group's name....and this is where it fails. I am able to iterate over around groups but the only available properties are things like 'id' there is no DisplayName property.
Any help here would be appreciated.
Could you please try to run the sample code :
var page = await graphClient
.Users[userObjectId]
.MemberOf
.Request()
.GetAsync();
var names = new List<string>();
names.AddRange(page
.OfType<Group>()
.Select(x => x.DisplayName)
.Where(name => !string.IsNullOrEmpty(name)));
while (page.NextPageRequest != null)
{
page = await page.NextPageRequest.GetAsync();
names.AddRange(page
.OfType<Group>()
.Select(x => x.DisplayName)
.Where(name => !string.IsNullOrEmpty(name)));
}
return names;
Sample question - How to get group names of the user is a member of using Microsoft Graph API?
Hope this helps.
Thanks
Related
I have an app that will operate almost entirely on Spotify OAuth, that will have features to alter the playback of your music.
I'm able to get Spotify OAuth working perfectly such that I can log into my app, but after I've logged in, I need access to the current user's spotify access_token so that I can forward it to my spotify requests.
I followed this guide from ms to try to save the tokens: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/?view=aspnetcore-6.0&tabs=visual-studio
And I have tried all these ways to then save that token into the HttpContext such that I can access it:
options.Events.OnCreatingTicket = ctx =>
{
List<AuthenticationToken> tokens = ctx.Properties.GetTokens().ToList();
tokens.Add(new AuthenticationToken()
{
Name = "TicketCreated",
Value = DateTime.UtcNow.ToString()
});
var spotifyAccessToken = tokens.FirstOrDefault(x => x.Name == "access_token").Value;
tokens.Add(new AuthenticationToken()
{
Name = "SpofityAccessToken",
Value = spotifyAccessToken
});
//store all the tokens as directed by MS
ctx.Properties.StoreTokens(tokens);
//store the properties into the HttpContext in 2 different ways
ctx.HttpContext.Items["Properties"] = ctx.Properties;
ctx.HttpContext.Features.Set(ctx.Properties);
//try adding a claim to the user
ctx.Identity.AddClaims(new[] { new Claim("SpotifyAccessToken", spotifyAccessToken) });
return Task.CompletedTask;
};
The problem I'm having is how do I then get this token out? all of these methods are not working:
[HttpGet]
public async Task Get()
{
await HttpContext.SignInAsync(User);
// try to re-run Authenticate, even though i'm already in an [Authorize] controller
var res = await HttpContext.AuthenticateAsync();
//props2 does not have the tokens i set
var props2 = res.Properties;
//props comes back null
var props = HttpContext.Features.Get<AuthenticationProperties>();
//claims has no SpotifyAccessToken claim
var claims = User.Claims.ToList();
var token = "hard-coded";
//here is where i need the token to then query spotify
var client = new SpotifyAPI.Web.SpotifyClient(token);
var res2 = await client.Player.GetCurrentPlayback();
}
I feel like I've tried everything, what am i doing wrong?
This is in a .NET 6 blazor wasm, .net core hosted app.
Also tried the solutions here to no avail Get AuthenticationProperties in current HttpRequest after HttpContext.SignInAsync
signInManager.UpdateExternalAuthenticationTokensAsync adds the the authentication tokens in [dbo].[AspNetUserTokens]
External login is where I call it:
// Sign in the user with this external login provider if the user already has a login.
var signInResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: true, bypassTwoFactor: true);
if (signInResult.Succeeded)
{
await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
_logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider);
return LocalRedirect(returnUrl);
}
Later on you can get it by using :
var token = await userManager
.GetAuthenticationTokenAsync(user, "Spotify", "access_token");
var expiresAtStr = await userManager
.GetAuthenticationTokenAsync(user, "Spotify", "expires_at");
If the token is stored in the Cookie then you can access the various tokens using:
string accessToken = await HttpContext.GetTokenAsync("access_token");
string idToken = await HttpContext.GetTokenAsync("id_token");
string refreshToken = await HttpContext.GetTokenAsync("refresh_token");
string tokenType = await HttpContext.GetTokenAsync("token_type");
string accessTokenExpire = await HttpContext.GetTokenAsync("expires_at");
However, you can not store data in ctx.HttpContext and assume it will be persisted across requests. either you sign-in the user using the cookie middleware or you store the tokens in the UserSession object.
See this article on how to configure and store data in the session, that will be persisted across requests.
Session and state management in ASP.NET Core
If you configure it properly, then you can use it like:
HttpContext.Session.SetString("token", token.Trim());
I have registered an application and provided the required permission in Azure and using that application to retrieve the modified users via delta Query.
Below is the code.
var pagedCollection = await graphClient.Users
.Request()
.Delta()
.Select("userPrincipalName,mobilePhone")
.GetAsync();
But the response does not contain the nextLink or deltaLink as mentioned in the documentation.
I can get the above links if I test the API in the graph explorer.
https://graph.microsoft.com/v1.0/users/delta
Response from Graph Explorer.
Am I missing anything here while calling the same API using C#?
Any help on this will be appreciated!
You can try below code:
List<Microsoft.Graph.User> usersList = new List<Microsoft.Graph.User>();
var users = await graphClient.Users.Delta().Request().Top(10).GetAsync();
// Add the first page of results to the user list
usersList.AddRange(users.CurrentPage);
try
{
while (users.AdditionalData.ContainsKey("#odata.nextLink") && users.AdditionalData["#odata.nextLink"].ToString() != null)
{
users.InitializeNextPageRequest(graphClient, users.AdditionalData["#odata.nextLink"].ToString());
users = await users.NextPageRequest.GetAsync();
usersList.AddRange(users.CurrentPage);
}
}
catch (Exception e)
{
}
Reference link: Microsoft Graph API Pagination is not working for getting all users from Azure AD
I have a connected console application that I'm writing to help automate an upcoming tenant migration.
What I'm looking to do is remove all associations of the custom domains from our current tenant so I can add them to a new tenant.
What I have so far:
ConsoleApp is registered with AAD and API permissions have been assigned and granted consent.
I've imported the following:
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client
Initialized a graphClient:
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
And successfully made .GetAsync calls to the Graph.
What I need help with:
I'm trying to update a Group's email to 'gItem.mailNickname#tenantID'. I've got an error saying that the "Mail field is read-only". Thru the Graph Explorer, I noticed that the proxyAddresses field includes all the aliases for the group and that the Mail is listed as "SMPT:..." I'm trying to overwrite the proxyAddresses field with
var upGroup = await graphClient
.Groups["4f629be4-f592-4520-b80d-7570f68e276e"]
.Request()
.GetAsync();
var updateFields = new Group
{
ProxyAddresses = new List<string>()
{
"SMPT:" + upGroup.MailNickname + "#" + tenantID,
}
};
await graphClient
.Groups[upGroup.Id]
.Request()
.UpdateAsync(updateFields);
I get the error that there are insufficient permissions but have checked AAD to ensure that Directory.ReadWrite.All and Groups.ReadWrite.All are both provisioned and granted.
Other Group properties are able to be altered which leads me to question my structure on the proxyAddresses property.
Failed Code to add to the list
var updateFields = new Group
{
ProxyAddresses = new string[] { "SMTP:" + upGroup.MailNickname + "#" + tenantID }
};
Full Code:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
namespace RemoveGroupAliases
{
class Program
{
private static string clientId = "";
private static string tenantID = "";
private static string clientSecret = "";
private static IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
static ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
static async Task Main(string[] args)
{
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var groups = await graphClient
.Groups
.Request()
.Filter("mailEnabled+eq+true")
.GetAsync();
foreach (var tgroup in groups)
{
Console.WriteLine(tgroup.Id);
Console.WriteLine(tgroup.DisplayName);
};
var upGroup = await graphClient
.Groups["4f629be4-f592-4520-b80d-7570f68e276e"]
.Request()
.GetAsync();
var updateFields = new Group
{
ProxyAddresses = new List<String>()
{
"SMTP:" + upGroup.MailNickname + "#" + tenantID
}
};
await graphClient
.Groups[upGroup.Id]
.Request()
.UpdateAsync(updateFields);
Console.ReadLine();
}
}
}
The idea is to pull all mail-enabled groups and remove all domains so I can remove it from this tenant and associate it to a new tenant. I'll have a different program to assign the domains and aliases once the domains are switched over. When I hover over the tooltip for proxyAddresses one of the last lines says "Read-Only" which may be my issue but I'm not getting that as the error.
The scenario you're describing simply isn't possible. You cannot detach a Group (or really any data) from one tenant and migrate it to another. Every AAD tenant is a distinct entity and isolated from any other tenant.
Microsoft Graph uses the token you provide to route your requests to the correct API/Service within the tenant that generated the token. You cannot have a single token connected to multiple tenants.
Also, as noted in the documentation, the properties mail and proxyAddresses are read-only. If you wish to change the group's email alias, you can change the mailNickname property. This is simply the "alias" for the Group, the domain portion of the email address is automatically assigned by AAD and Exchange ({mailNickname}#{default.tenant.domain}).
So, here is the problem. I searched all over the MSDN and here on Stack but there isn't one definitive answer how to get (or is it even possible as of today?) to access Shared calendars in Office365.
I followed this tutorial and here is offending method:
public async Task<ActionResult> Index()
{
List<MyCalendar> myCalendars = new List<MyCalendar>();
var signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.Authority, new ADALTokenCache(signInUserId));
try
{
DiscoveryClient discClient = new DiscoveryClient(SettingsHelper.DiscoveryServiceEndpointUri,
async () =>
{
var authResult = await authContext.AcquireTokenSilentAsync(SettingsHelper.DiscoveryServiceResourceId, new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey), new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return authResult.AccessToken;
});
var discoveryCapabilitiesResult = await discClient.DiscoverCapabilitiesAsync();
var dcr = await discClient.DiscoverCapabilityAsync("Calendar");
OutlookServicesClient exClient = new OutlookServicesClient(dcr.ServiceEndpointUri,
async () =>
{
var authResult = await authContext.AcquireTokenSilentAsync(dcr.ServiceResourceId, new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey), new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return authResult.AccessToken;
});
//var calendarsResult = await exClient.Me.Calendars.ExecuteAsync();
var calendarsResult = await exClient.Me.Calendars.ExecuteAsync();
do
{
var calendars = calendarsResult.CurrentPage;
foreach (var c in calendars)
{
myCalendars.Add(new MyCalendar { DisplayName = c.Name });
}
calendarsResult = await calendarsResult.GetNextPageAsync();
} while (calendarsResult != null);
}
catch (AdalException exception)
{
//handle token acquisition failure
if (exception.ErrorCode == AdalError.FailedToAcquireTokenSilently)
{
authContext.TokenCache.Clear();
//handle token acquisition failure
}
}
return View(myCalendars);
}
This function will return only Calendar under "My Calendars" but not the others (see the picture)
I get only the first one - that is mine calendar. Second one under "My Calendars" is shared with me (I'm not the author, somebody else is and I have r/w on it) and one under "Shared Calendars" is company-wide one (on this one I also have r/w permissions).
Is there a way to get all of them? On portal.azure.com my app is added and I set permissions for both mine and shared calendars:
I have no idea what to do. Contacts are working properly but I cannot find a way to get any shared calendar.
Based on the test, the Microsoft Graph could get the all calendars. Here is the rest for your reference:
Shared calendar from chenchenLi to NanYu:
Query the calendars from NanYu:
More detail about the Microsoft Graph REST to get the calendars, you can refer the link below:
List calendars
Is there any way, I can get the Home Time line for a user by passing his screenName? I currently have a code to get the timeline for authenticated user. But I also want to get for other users by using the same authenticated account. Is it possible? OR I need to introduce some other kind of authentication for this to work?
public async void GetHomeTimeLine(string screenName)
{
_screenName = screenName;
var twitterContext = new TwitterContext(_auth);
var tweets = await twitterContext.Status
.Where(s => s.ScreenName == _screenName
&& s.Type == StatusType.Home && s.Count == 200)
.ToListAsync();
}
The Twitter API doesn't offer an option to provide a screen name on Home timeline. You either need to authorize as the user who owns the timeline or do a Friends/FollowerList query to get IDs and then query the tweets of the friends based on their user id.