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

Blazor在OnTokenValidated事件调用受保护API遇Token获取异常

解决Blazor Azure AD B2C在OnTokenValidated事件中调用下游API的MsalUiRequiredException错误

问题原因

在OnTokenValidated事件触发时,Microsoft Identity Web尚未将用户账户信息持久化到TokenCache中,此时调用GetAccessTokenForUserAsync无法定位到用户账户,从而抛出user_null错误。另外,代码中用Task.Run().GetAwaiter().GetResult()同步阻塞异步方法,会导致请求上下文丢失,进一步加重问题。

解决方案

1. 手动构造用户账户信息传入Token获取方法

从TokenValidatedContext的Principal中提取用户对象ID(oid)和租户ID(tid),手动创建IAccount实例,传递给GetAccessTokenForUserAsync重载方法,确保能匹配到对应的用户账户。

2. 全程保持异步调用

移除同步阻塞代码,维持异步执行流程,避免上下文丢失。

3. (可选)将API调用逻辑移至后续阶段

如果业务允许,可把下游API调用逻辑放到用户登录完成后的中间件、页面初始化方法(如OnInitializedAsync)中,此时用户上下文已完全建立,无需手动处理账户信息。

修正后的代码

Program.cs(保持原有配置不变)

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(options =>
    {
        builder.Configuration.GetSection("AzureAd").Bind(options);

        var prevOnTokenValidatedHandler = options.Events.OnTokenValidated;

        options.Events.OnTokenValidated = async context =>
        {
            await prevOnTokenValidatedHandler(context).ConfigureAwait(false);
            await AuthorizationHelper.ValidateAADAppToken(context);
        };
    }
    )
        .EnableTokenAcquisitionToCallDownstreamApi()
            .AddInMemoryTokenCaches();

AuthorizationHelper.cs(修正后的逻辑)

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;
using Microsoft.Identity.Client;
using System.Security.Claims;

internal static class AuthorizationHelper
{
    internal static async Task ValidateAADAppToken(TokenValidatedContext context)
    {
        var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>();
        var scopes = new[] { "https://<Tenant>/<ApiId>/access_as_user" };

        // 从当前Principal提取用户核心标识
        var oid = context.Principal.FindFirstValue(ClaimConstants.ObjectId);
        var tid = context.Principal.FindFirstValue(ClaimConstants.TenantId);
        var upn = context.Principal.FindFirstValue(ClaimConstants.Upn);

        // 手动构造用户账户实例
        var account = new Account(oid, tid, upn);

        // 异步获取AccessToken,传入手动构造的账户
        var accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes, account: account);

        // 调用下游API
        using var httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
        var response = await httpClient.GetAsync("https://localhost:7297/WeatherForecast");

        // 示例:处理API返回的权限信息,添加到用户Claims中
        if (response.IsSuccessStatusCode)
        {
            var userPermissions = await response.Content.ReadFromJsonAsync<UserPermissions>();
            var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
            claimsIdentity?.AddClaims(userPermissions.Roles.Select(r => new Claim(ClaimTypes.Role, r)));
            claimsIdentity?.AddClaims(userPermissions.Groups.Select(g => new Claim("groups", g)));
        }
    }

    // 匹配API返回结构的模型类
    private class UserPermissions
    {
        public List<string> Roles { get; set; } = new();
        public List<string> Groups { get; set; } = new();
        // 可添加其他权限字段
    }
}

额外注意事项

  • 确保Azure AD B2C应用注册中已配置好下游API的权限,且用户已被授予对应权限。
  • 若使用Microsoft.Identity.Web最新版本,部分API可能有细微调整,请根据实际版本适配代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 14:42:09