0

我有一种情况,我可以在我的 Startup 中成功使用 GraphServiceClient,但不能在我的 Razor 页面中使用。我正在将 Microsoft Identity Web 与 Microsoft Graph 一起使用,并在此处遵循了 MS 文档,还检查了许多其他类似的问题,但我总是以同样的错误告终......

MsalUiRequiredException:没有帐户或登录提示传递给 AcquireTokenSilent 调用

请注意,我的应用注册在我的租户中,但用户帐户在客户租户中。因此,我的应用身份验证配置为“任何组织目录中的帐户(任何 Azure AD 目录 - 多租户)”和以下 API 权限...

在此处输入图像描述

我的设置如下,虽然它是一个多租户应用程序,但我只有一个客户,因此将 TenantId 设置为我的客户租户 ID,以获得简化的登录体验。

"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<Customer Tenant Id>",
"ClientId": "<My App Reg Id>",
"ClientSecret": "<My App Reg Secret>",
"CallbackPath": "/signin-oidc"
},
"DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "https://graph.microsoft.com/.default"
}

我的 Startup 具有以下 ConfigureServices 方法。请注意,对 graphServiceClient.Me.Request().GetAsync() 的调用在这里有效,并从我的客户 Azure AD 返回用户信息。当用户登录到我的应用程序时,我计划使用它来用额外的用户信息(即照片)填充我的本地数据库。

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthorization(options =>
        {
            options.FallbackPolicy = options.DefaultPolicy;
        });
        services.AddRazorPages()
            .AddMvcOptions(options => { })
            .AddMicrosoftIdentityUI();

        var initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(',');
        services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
            .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
            .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
            .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
            .AddInMemoryTokenCaches();

        services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
        {
            options.TokenValidationParameters.IssuerValidator = ValidateSpecificIssuers; //restrict access for multi-tenant app
            options.Events = new OpenIdConnectEvents
            {
                OnTokenValidated = async context =>
                {
                    GraphServiceClient graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider(async request =>
                    {
                        // Add the access token in the Authorization header of the API request.
                        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.TokenEndpointResponse.AccessToken);
                    }));

                    try
                    {
                        var user = await graphServiceClient.Me.Request().GetAsync(); // THIS CALL WORKS!
                    }
                    catch (Exception ex)
                    {
                        throw new UnauthorizedAccessException("Cannot get user info from AAD");
                    }

                    await Task.FromResult(0);
                }
            };
        });


    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
            endpoints.MapControllers();
        });
    }

    private string ValidateSpecificIssuers(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
    {
        var validIssuers = Configuration["AllowedIssuers"];
        if (validIssuers != null && validIssuers.Split(',').Select(tid => $"https://login.microsoftonline.com/{tid}").Contains(issuer))
        {
            return issuer;
        }
        else
        {
            throw new SecurityTokenInvalidIssuerException("The user account does not belong to an allowed tenant");
        }
    }

}

这是我的 Razor 页面,但它总是在同一个图形调用上失败,即使它在我的启动文件中工作。我已经尝试将范围从“https://graph.microsoft.com/v1.0”更改为“user.read”,但在这一行仍然失败。

var user = await _graphServiceClient.Me.Request().GetAsync();

[AuthorizeForScopes(Scopes = new[] { "https://graph.microsoft.com/.default" })]
public class IndexModel : PageModel
{
    private readonly ILogger<IndexModel> _logger;
    private readonly GraphServiceClient _graphServiceClient;

    public IndexModel(ILogger<IndexModel> logger, GraphServiceClient graphServiceClient)
    {
        _logger = logger;
        _graphServiceClient = graphServiceClient;
    }

    public async void OnGet()
    {
        try
        {
            var user = await _graphServiceClient.Me.Request().GetAsync(); // THIS CALL FAILS!
            using (var photoStream = await _graphServiceClient.Me.Photo.Content.Request().GetAsync())
            {
                byte[] photoByte = ((MemoryStream)photoStream).ToArray();
                ViewData["photo"] = Convert.ToBase64String(photoByte);
            }
            ViewData["name"] = user.DisplayName;
        }
        catch (Exception e)
        {
            _logger.LogError(e.Message);
        }

    }
}

这是错误的堆栈跟踪。

Microsoft.Graph.ServiceException
  HResult=0x80131500
  Message=Code: generalException
Message: An error occurred sending the request.

  Source=Microsoft.Graph.Core
  StackTrace:
   at Microsoft.Graph.HttpProvider.<SendRequestAsync>d__19.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at Microsoft.Graph.HttpProvider.<SendAsync>d__18.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at Microsoft.Graph.BaseRequest.<SendRequestAsync>d__40.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at Microsoft.Graph.BaseRequest.<SendAsync>d__34`1.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at Microsoft.Graph.UserRequest.<GetAsync>d__5.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Test365.Pages.IndexModel.<OnGet>d__3.MoveNext() in C:\Users\ojmcf\source\repos\Test365\Pages\Index.cshtml.cs:line 28

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent. 

Inner Exception 2:
MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call. 
4

2 回答 2

2

最后简单修复。我假设 Razor Pages 正在全局处理授权作为启动文件中的设置,所以我忽略[Authorize]了页面模型顶部的属性。一旦我添加它开始按预期工作。

但是,为了方便起见,我最终[Authorize]从页面模型中删除了该属性,而是使用RequireAuthorization()启动文件的配置方法中的端点路由下全局添加了它。

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages().RequireAuthorization();
        endpoints.MapControllers();
    });
于 2021-11-05T00:03:18.160 回答
0

更新

尝试添加/v2.0您的网址,如下所示。

validIssuers.Split(',').Select(tid => $"https://login.microsoftonline.com/{tid}/v2.0").Contains(issuer)

替换以下代码

[AuthorizeForScopes(Scopes = new[] { "https://graph.microsoft.com/v1.0" })]

[AuthorizeForScopes(Scopes = new[] { "user.read" })]

,并对其进行测试。

提示

这个 url( https://graph.microsoft.com/v1.0) 应该在appsettings.json文件中。

于 2021-11-04T01:36:16.333 回答