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

调用GetAuthenticationResultForAppAsync时AccessToken有效但IdToken为空的问题

使用Microsoft.Identity.Web实现AzureAD认证时IdToken为空导致空指针异常的解决方案

问题描述

尝试使用Microsoft.Identity.Web实现AzureAD认证,调用GetAuthenticationResultForAppAsync返回的结果中AccessToken有值但IdToken为null,导致令牌验证时出现空指针异常。

代码片段

services.AddOpenIdConnect(options =>
{
    var x = Configuration["AzureAd:CallbackPath"];

    options.ClientId = Configuration["AzureAd:ClientId"];
    options.ClientSecret = Configuration["AzureAd:ClientSecret"];
    options.CallbackPath = new PathString(Configuration["AzureAd:CallbackPath"]);

    options.Authority = Configuration["AzureAd:Instance"] + Configuration["AzureAd:TenantId"];
    options.ResponseType = "code id_token";
    options.Authority = options.Authority + "/v2.0/";

    //options.Scope.Clear();
    //options.Scope.Add("User.ReadBasic.All");
    options.Scope.Add("user.read");
    options.RequireHttpsMetadata = false;       // 开发环境配置

    options.Events = new OpenIdConnectEvents
    {
        OnTokenValidated = OnTokenValidated
    };

    // 由MSAL.NET处理授权码兑换,使令牌可在令牌缓存中供后续控制器使用
    var handler = options.Events.OnAuthorizationCodeReceived;
    options.Events.OnAuthorizationCodeReceived = async context =>
    {
        context.HandleCodeRedemption();
        var _tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>();
        //await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, options.Scope);
        var result = await _tokenAcquisition.GetAuthenticationResultForAppAsync("https://americancancer.onmicrosoft.com/xxxx/.default");
        //var result = await _tokenAcquisition.GetAuthenticationResultForUserAsync(options.Scope);

        // 不要将AccessToken共享给ASP.NET Core,否则ASP.NET会缓存它,后续增量授权时无法发送OAuth2.0请求
        // 共享ID Token以让应用知晓用户身份(在HttpContext.User中)
        context.HandleCodeRedemption(null, result.IdToken);

        await handler(context);
    };
});

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddMicrosoftIdentityWebApi(Configuration, "AzureAd")
                .EnableTokenAcquisitionToCallDownstreamApi()
                .AddInMemoryTokenCaches();

错误信息

Microsoft.AspNetCore.Authentication.AuthenticationFailureException: 处理远程登录时遇到错误。
---> System.ArgumentNullException: IDX10000: 参数'token'不能为null或空对象。(参数'token')
at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.ValidateTokenUsingHandlerAsync(String idToken, AuthenticationProperties properties, TokenValidationParameters validationParameters)
at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.HandleRemoteAuthenticateAsync()
--- 内部异常堆栈跟踪结束 ---
at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

问题原因及修复方案

问题根源

GetAuthenticationResultForAppAsync是用于客户端凭证流(Client Credentials Flow)的方法,该流属于无用户参与的应用级认证,不会返回IdToken,因此result.IdToken必然为null,这就是导致空指针异常的原因。而当前场景是用户登录的授权码流(Authorization Code Flow),需要处理与用户身份相关的令牌。

修复步骤

  1. 替换错误的令牌获取方法
    取消注释await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, options.Scope);,移除GetAuthenticationResultForAppAsync的调用,该方法会将用户的令牌缓存到MSAL中,同时可以直接从上下文获取IdToken。

  2. 正确传递IdToken
    在context.HandleCodeRedemption中,直接使用context.TokenEndpointResponse.IdToken,这是Azure AD返回的原始IdToken,无需通过应用流方法获取。

修改后的OnAuthorizationCodeReceived事件代码如下:

options.Events.OnAuthorizationCodeReceived = async context =>
{
    context.HandleCodeRedemption();
    var _tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>();
    // 将用户令牌缓存到MSAL中
    await _tokenAcquisition.AddAccountToCacheFromAuthorizationCode(context, options.Scope);

    // 使用AD返回的原始IdToken,避免空指针
    context.HandleCodeRedemption(null, context.TokenEndpointResponse.IdToken);

    await handler(context);
};

额外说明

  • 如果需要获取用户的AccessToken用于调用下游API,后续可以通过ITokenAcquisition.GetAccessTokenForUserAsync方法从缓存中获取,无需在当前事件中手动处理。
  • 确保options.ResponseType = "code id_token"的配置正确,该配置要求AD在授权码响应中返回IdToken,这是获取用户身份的前提。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:55:55