通过MS Graph和ASP.NET Core获取Azure AD组的异常

问题描述

我要在用户登录时检索分配给登录用户的所有组。以便这些组可用于用户登录会话。

我遵循了下面链接中的代码

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

,并进行了如下修改,以调用GetMyMemberOfGroupsAsync(),但底部显示异常。

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

  ...
}

下面是添加代码示例中以获取所有组的代码,但是在_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;
        }
    }

下面的异常抛出到_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,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,ClaimsPrincipal user)
   at Microsoft.Identity.Web.TokenAcquisition.GetAccesstokenForUserAsync(IEnumerable`1 scopes,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,CancellationToken cancellationToken)
   at Microsoft.Graph.HttpProvider.SendAsync(HttpRequestMessage request,CancellationToken cancellationToken)
   at Microsoft.Graph.BaseRequest.SendRequestAsync(Object serializableObject,CancellationToken cancellationToken,HttpCompletionoption completionoption)
   at Microsoft.Graph.BaseRequest.SendAsync[T](Object serializableObject,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)

解决方法

如果您的应用请求的范围超出了管理员同意的范围,则会收到MsalUiRequiredException,因此请确保已为您的应用授予足够的permissions并获得了管理员同意

如果在用户登录时需要检索分配给已登录用户的所有组,请参考以下代码:

GraphServiceClient graphClient = new GraphServiceClient( authProvider );

var securityEnabledOnly = true;

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