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

调用ITokenAcquisition.GetAccessTokenForUserAsync遇系列异常求助

问题:Web API调用GetAccessTokenForUserAsync时出现系列异常

我在Web API的Startup.cs中编写了如下代码,运行时调用GetAccessTokenForUserAsync抛出NullReferenceException异常,请问遗漏了什么配置?

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.Events = new JwtBearerEvents()
    {
        OnTokenValidated = async ctx =>
        {
            var tokenAcquisition = ctx.HttpContext.RequestServices
                .GetRequiredService<ITokenAcquisition>();

            var graphClient = new GraphServiceClient(
                new DelegateAuthenticationProvider(async (request) =>
                {
                    var token = await tokenAcquisition
                        .GetAccessTokenForUserAsync(new[] { "User.Read", "User.ReadBasic.All", "GroupMember.Read.All" }, user: ctx.Principal);
                    request.Headers.Authorization =
                        new AuthenticationHeaderValue("Bearer", token);
                }));

            // 获取用户组信息
            var groups = await graphClient.Me.CheckMemberGroups(new[] { Constants.GroupId })
            .Request()
            .PostAsync();
        }
    };
});

services
.AddAuthentication(x =>
{
    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddMicrosoftIdentityWebApi(this.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph()
.AddInMemoryTokenCaches();

首次异常:NullReferenceException


更新1:添加authenticationScheme参数后出现新异常

发现调用GetAccessTokenForUserAsync时缺少authenticationScheme: JwtBearerDefaults.AuthenticationScheme参数,添加后又触发异常提示:

IDW10104: Both client secret and client certificate cannot be null or whitespace, and only ONE must be included in the configuration of the web app when calling a web API. For instance, in the appsettings.json file.

疑问:我已经使用调用方的用户令牌了,为什么还需要客户端密钥或证书?能否不用?Web API已获得用户的委托权限,且收到的令牌来自已认证用户。


更新2:添加客户端密钥后仍有异常

添加客户端密钥后,依旧出现NullReferenceException,同时新增异常提示IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user


更新3:附上更新后的完整ConfigureServices代码

public void ConfigureServices(IServiceCollection services)
{
    var principalProvider = new ClaimsPrincipalProvider();

    services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.Events = new JwtBearerEvents()
        {
            OnTokenValidated = async ctx =>
            {
                try
                {
                    var tokenAcquisition = ctx.HttpContext.RequestServices
                        .GetRequiredService<ITokenAcquisition>();

                    var graphClient = new GraphServiceClient(
                    new DelegateAuthenticationProvider(async (request) =>
                    {
                        var token = await tokenAcquisition
                            .GetAccessTokenForUserAsync(new[] { "User.Read", "User.ReadBasic.All", "GroupMember.Read.All" }, user: ctx.Principal, authenticationScheme: JwtBearerDefaults.AuthenticationScheme);

                        request.Headers.Authorization =
                            new AuthenticationHeaderValue("Bearer", token);
                    }));

                    var groups = await graphClient.Me.CheckMemberGroups(
                        new[]
                        {
                            Constants.GroupId
                        })
                    .Request()
                    .PostAsync();
                }
                catch (System.Exception ex)
                {

                }
            }
        };
    });

    services
    .AddAuthentication(x =>
    {
        x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddMicrosoftIdentityWebApi(this.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph()
.AddInMemoryTokenCaches();

    services.AddAuthorization(options =>
    {
        var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme);
        defaultAuthorizationPolicyBuilder = defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
        options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
    });
}

希望能解决这些异常问题。


解决方案

1. 修复首次NullReferenceException

你直接覆盖了options.Events,但AddMicrosoftIdentityWebApi已经默认配置了JwtBearer的事件逻辑,覆盖后会导致ITokenAcquisition所需的上下文信息丢失,引发空引用。

正确做法:不要直接替换整个Events对象,而是在原有事件逻辑基础上添加自定义代码:

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
    // 保存原有OnTokenValidated委托
    var existingOnTokenValidated = options.Events.OnTokenValidated;
    
    options.Events.OnTokenValidated = async ctx =>
    {
        // 先执行框架原有校验逻辑
        await existingOnTokenValidated(ctx);
        
        // 再执行你的自定义逻辑
        var tokenAcquisition = ctx.HttpContext.RequestServices
            .GetRequiredService<ITokenAcquisition>();

        var graphClient = new GraphServiceClient(
            new DelegateAuthenticationProvider(async (request) =>
            {
                var token = await tokenAcquisition
                    .GetAccessTokenForUserAsync(new[] { "User.Read", "User.ReadBasic.All", "GroupMember.Read.All" }, 
                                               user: ctx.Principal, 
                                               authenticationScheme: JwtBearerDefaults.AuthenticationScheme);
                request.Headers.Authorization =
                    new AuthenticationHeaderValue("Bearer", token);
            }));

        var groups = await graphClient.Me.CheckMemberGroups(new[] { Constants.GroupId })
            .Request()
            .PostAsync();
    };
});

2. 解释IDW10104异常并处理

Web API调用下游API(如Microsoft Graph)时,必须使用**On-Behalf-Of(OBO)**流程。这个流程中,Web API需要用自身的客户端凭据(密钥或证书)向Azure AD证明身份,才能用收到的用户令牌换取下游API的访问令牌,因此客户端凭据是必须配置的,无法省略。

你需要在appsettings.json的AzureAd节点中添加客户端密钥:

"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "你的域名",
  "TenantId": "你的租户ID",
  "ClientId": "你的WebAPI客户端ID",
  "ClientSecret": "你的WebAPI客户端密钥" // 新增该行
}

3. 解决IDW10502异常

这个异常表示需要用户交互式登录,但Web API是无界面服务,出现该问题通常是以下原因:

  • 用户令牌的受众(aud声明)不是你的Web API客户端ID
  • 用户令牌缺少委托权限对应的scp声明
  • Web API未正确配置Microsoft Graph的委托权限,或未授予管理员同意
  • OBO流程的权限传递逻辑错误

检查与修复步骤:

  1. 确认Web API在Azure AD中已添加Microsoft Graph的委托权限(User.Read、GroupMember.Read.All等),并且已经授予管理员同意
  2. 确认调用Web API的客户端应用,已请求Web API的权限(或直接请求了Graph的权限并通过OBO传递)
  3. 避免手动实例化GraphServiceClient,直接使用框架注入的实例(AddMicrosoftGraph()已完成注册):
var graphClient = ctx.HttpContext.RequestServices.GetRequiredService<GraphServiceClient>();

4. 最终优化后的ConfigureServices代码

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"))
        .EnableTokenAcquisitionToCallDownstreamApi(options =>
        {
            options.Scopes = new[] { "User.Read", "User.ReadBasic.All", "GroupMember.Read.All" };
        })
        .AddMicrosoftGraph(options =>
        {
            options.Scopes = string.Join(" ", new[] { "User.Read", "User.ReadBasic.All", "GroupMember.Read.All" });
        })
        .AddInMemoryTokenCaches();

    // 配置JwtBearer事件,保留原有逻辑
    services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        var originalOnTokenValidated = options.Events.OnTokenValidated;
        options.Events.OnTokenValidated = async ctx =>
        {
            await originalOnTokenValidated(ctx);

            try
            {
                var graphClient = ctx.HttpContext.RequestServices.GetRequiredService<GraphServiceClient>();
                var groups = await graphClient.Me.CheckMemberGroups(new[] { Constants.GroupId })
                    .Request()
                    .PostAsync();
                
                // 可将组校验结果添加到用户声明中
                var claimsIdentity = ctx.Principal.Identity as ClaimsIdentity;
                claimsIdentity.AddClaim(new Claim("isMemberOfTargetGroup", groups.Contains(Constants.GroupId).ToString()));
            }
            catch (Exception ex)
            {
                // 不要吞掉异常,建议记录日志后抛出
                // _logger.LogError(ex, "校验用户组身份时出错");
                throw;
            }
        };
    });

    services.AddAuthorization(options =>
    {
        options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
            .RequireAuthenticatedUser()
            .Build();
    });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:45:40