You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法让Blazor Server应用通过Azure AD B2C调用受保护的下游Web API

问题描述

使用dotnet new创建Blazor Server项目后,可通过AAD B2C用户流正常登录,但点击“Call Web API”按钮时,在CallWebAPI.razor的await downstreamAPI.CallWebApiForUserAsync处抛出异常。

创建项目的命令

dotnet new blazorserver --output "PlayingWithBlazor" --framework net6.0 --auth IndividualB2C --aad-b2c-instance "https://{mytenant}.b2clogin.com/" --domain "{mydomain}.onmicrosoft.com" --client-id "{myClientID}" --susi-policy-id "B2C_1_SignUp" --called-api-url "https://localhost:7042/api" --called-api-scopes "https://{mytennant}.onmicrosoft.com/a82e00f8-939d-47ab-b2f3-4e557020f729/access_as_user"

CallWebApi.razor的@Code代码块

@code {
private HttpResponseMessage? response;
private string? apiResult;

protected override async Task OnInitializedAsync()
{
    
    try
    {
     
        response = await downstreamAPI.CallWebApiForUserAsync(
                "DownstreamApi",
                options => options.RelativePath = "/Subscriber");

        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            apiResult = await response.Content.ReadAsStringAsync();
        }
        else
        {
            apiResult = "Failed to call the web API";
        }
    }
    catch (Exception ex)
    {
        ConsentHandler.HandleException(ex);
    }
}

异常信息

Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException
  HResult=0x80131500
  Message=IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. 
  Source=Microsoft.Identity.Web
  StackTrace:
   at Microsoft.Identity.Web.TokenAcquisition.<GetAuthenticationResultForUserAsync>d__16.MoveNext()
   at Microsoft.Identity.Web.DownstreamWebApi.<CallWebApiForUserAsync>d__5.MoveNext()
   at PlayingWithBlazor.Pages.CallWebApi.<OnInitializedAsync>d__3.MoveNext() in C:\Users\AndySchneider\source\repos\PlayingWithBlazor\Pages\CallWebApi.razor:line 33

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

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

Web API代码

// GET: api/<SubscriberController>
[Authorize]
[HttpGet]
public IEnumerable<Subscriber> Get()
{
    return subscriberRepository.GetAllAsync().Result;
}

Program.cs代码

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using PlayingWithBlazor.Data;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Identity.Client;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ');

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAdB2C"))
        .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
            .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
            .AddInMemoryTokenCaches();

builder.Services.AddControllersWithViews()
    .AddMicrosoftIdentityUI();


builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy
    options.FallbackPolicy = options.DefaultPolicy;
});

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor()
    .AddMicrosoftIdentityConsentHandler();
builder.Services.AddSingleton<WeatherForecastService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios
    app.UseHsts();
}


app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

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

app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

解决方案

异常核心原因是调用AcquireTokenSilent时未传入账户或登录提示信息,以下是具体修复步骤:

1. 传递用户上下文到API调用

在Blazor Server中需确保用户身份上下文被正确传递,修改CallWebApi.razor:

// 注入依赖
@inject IHttpContextAccessor HttpContextAccessor
@inject NavigationManager NavigationManager

@code {
private HttpResponseMessage? response;
private string? apiResult;

protected override async Task OnInitializedAsync()
{
    
    try
    {
        // 显式传递当前用户
        response = await downstreamAPI.CallWebApiForUserAsync(
                "DownstreamApi",
                options => options.RelativePath = "/Subscriber",
                user: HttpContextAccessor.HttpContext?.User);

        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            apiResult = await response.Content.ReadAsStringAsync();
        }
        else
        {
            apiResult = "调用Web API失败";
        }
    }
    catch (MicrosoftIdentityWebChallengeUserException)
    {
        // 触发重新登录以获取令牌
        NavigationManager.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(NavigationManager.Uri)}");
    }
    catch (Exception ex)
    {
        apiResult = $"调用失败: {ex.Message}";
    }
}

同时在Program.cs中注册IHttpContextAccessor:

builder.Services.AddHttpContextAccessor();

2. 验证配置文件与权限

  • 检查appsettings.json中DownstreamApi:Scopes是否与创建项目时指定的作用域一致,注意修正创建命令中拼写错误的{mytennant}为{mytenant}。
  • 在Azure AD B2C控制台确认Blazor应用已被授予目标Web API的访问权限,且用户已完成权限同意。

3. 优化令牌缓存与异常处理

当前使用的AddInMemoryTokenCaches()适用于开发环境,生产环境可考虑使用分布式缓存。同时扩展异常处理逻辑,确保用户能被引导至登录流程获取所需令牌。


内容的提问来源于stack exchange,提问作者Andy Schneider

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 09:40:47