Exception getting Azure AD groups via MS Graph with ASP.NET Core

1.7k views Asked by At

I want to retrieve all groups assigned to a logged in user when user signed in. So that the groups can be used for the user's logged-in session.

I followed the code from the link below,

https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/5-WebApp-AuthZ/5-2-Groups

and modified to call GetMyMemberOfGroupsAsync() like below, but get exception shown at the bottom.

     public void ConfigureServices(IServiceCollection services)
        {
...
 // Sign-in users with the Microsoft identity platform
            services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
                    .AddMicrosoftIdentityWebApp(
                options =>
                {
                    Configuration.Bind("AzureAd", options);
                    options.Events = new OpenIdConnectEvents();
                    options.Events.OnTokenValidated = async context =>
                    {              
                        var graphService = sp.GetService<IGraphService>();
                        await graphService.GetMyMemberOfGroupsAsync();
                    };
                }, options => { Configuration.Bind("AzureAd", options); })
                    .EnableTokenAcquisitionToCallDownstreamApi(options => Configuration.Bind("AzureAd", options), initialScopes)
                    .AddMicrosoftGraph(Configuration.GetSection("GraphAPI"))
                    .AddInMemoryTokenCaches();

  ...
}

Below is the code added to the code example to get all groups, but exception is thrown at the line of _graphServiceClient.Me.MemberOf.Request().GetAsync();

  public class GraphService : IGraphService
    {
        private readonly GraphServiceClient _graphServiceClient;

        public GraphService(GraphServiceClient graphServiceClient)
        {
            _graphServiceClient = graphServiceClient;
        }
        public async Task<List<string>> GetMyMemberOfGroupsAsync()
        {
            List<string> groups = new List<string>();

            // Get groups the current user is a direct member of.
            IUserMemberOfCollectionWithReferencesPage memberOfGroups = await _graphServiceClient.Me.MemberOf.Request().GetAsync();  //exception thrown
            if (memberOfGroups?.Count > 0)
            {
                foreach (var directoryObject in memberOfGroups)
                {
                    // We only want groups, so ignore DirectoryRole objects.
                    if (directoryObject is Group)
                    {
                        Group group = directoryObject as Group;
                        groups.Add(group.DisplayName);
                    }
                }
            }

            // If paginating
            while (memberOfGroups.NextPageRequest != null)
            {
                memberOfGroups = await memberOfGroups.NextPageRequest.GetAsync();

                if (memberOfGroups?.Count > 0)
                {
                    foreach (var directoryObject in memberOfGroups)
                    {
                        // We only want groups, so ignore DirectoryRole objects.
                        if (directoryObject is Group)
                        {
                            Group group = directoryObject as Group;
                            groups.Add(group.DisplayName);
                        }
                    }
                }
            }
            return groups;
        }
    }

Exception below is thrown at the line of _graphServiceClient.Me.MemberOf.Request().GetAsync();

System.Exception: An error was encountered while handling the remote login.
 ---> Status Code: 0
Microsoft.Graph.ServiceException: Code: generalException
Message: An error occurred sending the request.

 ---> Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent. 
 ---> MSAL.NetCore.4.18.0.0.MsalUiRequiredException: 
    ErrorCode: user_null
Microsoft.Identity.Client.MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call.
   at Microsoft.Identity.Client.AcquireTokenSilentParameterBuilder.Validate()
   at Microsoft.Identity.Client.AbstractAcquireTokenParameterBuilder`1.ValidateAndCalculateApiId()
   at Microsoft.Identity.Client.AbstractClientAppBaseAcquireTokenParameterBuilder`1.ExecuteAsync(CancellationToken cancellationToken)
   at Microsoft.Identity.Client.AbstractAcquireTokenParameterBuilder`1.ExecuteAsync()
   at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForWebAppWithAccountFromCacheAsync(IConfidentialClientApplication application, IAccount account, IEnumerable`1 scopes, String authority, String userFlow)
   at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForWebAppWithAccountFromCacheAsync(IConfidentialClientApplication application, ClaimsPrincipal claimsPrincipal, IEnumerable`1 scopes, String authority, String userFlow)
   at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable`1 scopes, String tenant, String userFlow, ClaimsPrincipal user)
    StatusCode: 0 
    ResponseBody:  
    Headers: 
   --- End of inner exception stack trace ---
   at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable`1 scopes, String tenant, String userFlow, ClaimsPrincipal user)
   at Microsoft.Identity.Web.TokenAcquisition.GetAccessTokenForUserAsync(IEnumerable`1 scopes, String tenant, String userFlow, ClaimsPrincipal user)
   at Microsoft.Identity.Web.TokenAcquisitionCredentialProvider.AuthenticateRequestAsync(HttpRequestMessage request)
   at Microsoft.Graph.AuthenticationHandler.SendAsync(HttpRequestMessage httpRequestMessage, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
   at Microsoft.Graph.HttpProvider.SendRequestAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at Microsoft.Graph.HttpProvider.SendRequestAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
   at Microsoft.Graph.HttpProvider.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
   at Microsoft.Graph.BaseRequest.SendRequestAsync(Object serializableObject, CancellationToken cancellationToken, HttpCompletionOption completionOption)
   at Microsoft.Graph.BaseRequest.SendAsync[T](Object serializableObject, CancellationToken cancellationToken, HttpCompletionOption completionOption)
   at Microsoft.Graph.UserMemberOfCollectionWithReferencesRequest.GetAsync(CancellationToken cancellationToken)
   at WebApp_OpenIDConnect_DotNet.Controllers.GraphService.GetMyMemberOfGroupsAsync() in C:\_MyLab\AzureCode\ActiveDirectory\active-directory-aspnetcore-webapp-openidconnect-v2-v2\5-WebApp-AuthZ\5-2-Groups\Controllers\GraphService.cs:line 20
   at WebApp_OpenIDConnect_DotNet.Startup.<>c__DisplayClass4_0.<<ConfigureServices>b__8>d.MoveNext() in C:\_MyLab\AzureCode\ActiveDirectory\active-directory-aspnetcore-webapp-openidconnect-v2-v2\5-WebApp-AuthZ\5-2-Groups\Startup.cs:line 62
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilder.<>c__DisplayClass10_1.<<WebAppCallsWebApiImplementation>b__2>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.RunTokenValidatedEventAsync(OpenIdConnectMessage authorizationResponse, OpenIdConnectMessage tokenEndpointResponse, ClaimsPrincipal user, AuthenticationProperties properties, JwtSecurityToken jwt, String nonce)
   at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.HandleRemoteAuthenticateAsync()
   --- End of inner exception stack trace ---
   at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
1

There are 1 answers

0
Carl Zhao On

If your app requests more scopes than what the admin has consented, you'll receive an MsalUiRequiredException, so make sure that you have granted sufficient permissions to your application and granted admin consent to that permission.

If you need to retrieve all the groups assigned to the logged-in user when the user logs in, please refer to the following code:

GraphServiceClient graphClient = new GraphServiceClient( authProvider );

var securityEnabledOnly = true;

await graphClient.Me
    .GetMemberGroups(securityEnabledOnly)
    .Request()
    .PostAsync();